Import functionality for wino accounts, calendar sync UI, bunch of shell improvements

This commit is contained in:
Burak Kaan Köse
2026-04-04 20:23:20 +02:00
parent 1667aa34db
commit 1d0fcfb5b0
68 changed files with 2792 additions and 519 deletions
+556
View File
@@ -0,0 +1,556 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// LottieGen version:
// 8.2.250604.1+b02a3ee244
//
// Command:
// LottieGen -Language CSharp -Public -WinUIVersion 2.4 -InputFile sync.json
//
// Input file:
// sync.json (2404 bytes created 20:18+02:00 Apr 4 2026)
//
// LottieGen source:
// http://aka.ms/Lottie
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
// ___________________________________________________________
// | Object stats | UAP v15 count | UAP v7 count |
// |__________________________|_______________|______________|
// | All CompositionObjects | 26 | 26 |
// |--------------------------+---------------+--------------|
// | Expression animators | 1 | 1 |
// | KeyFrame animators | 1 | 1 |
// | Reference parameters | 1 | 1 |
// | Expression operations | 0 | 0 |
// |--------------------------+---------------+--------------|
// | Animated brushes | - | - |
// | Animated gradient stops | - | - |
// | ExpressionAnimations | 1 | 1 |
// | PathKeyFrameAnimations | - | - |
// |--------------------------+---------------+--------------|
// | ContainerVisuals | 1 | 1 |
// | ShapeVisuals | 1 | 1 |
// |--------------------------+---------------+--------------|
// | ContainerShapes | 1 | 1 |
// | CompositionSpriteShapes | 2 | 2 |
// |--------------------------+---------------+--------------|
// | Brushes | 1 | 1 |
// | Gradient stops | - | - |
// | CompositionVisualSurface | - | - |
// -----------------------------------------------------------
using Microsoft.Graphics.Canvas.Geometry;
using System;
using System.Collections.Generic;
using System.Numerics;
using Windows.Graphics;
using Windows.UI;
using Windows.UI.Composition;
namespace AnimatedVisuals
{
// Name: main_libary_shelf_icon_sync
// Frame rate: 60 fps
// Frame count: 61
// Duration: 1016.7 mS
sealed class Sync
: Microsoft.UI.Xaml.Controls.IAnimatedVisualSource
{
// Animation duration: 1.017 seconds.
internal const long c_durationTicks = 10166666;
public Microsoft.UI.Xaml.Controls.IAnimatedVisual TryCreateAnimatedVisual(Compositor compositor)
{
object ignored = null;
return TryCreateAnimatedVisual(compositor, out ignored);
}
public Microsoft.UI.Xaml.Controls.IAnimatedVisual TryCreateAnimatedVisual(Compositor compositor, out object diagnostics)
{
diagnostics = null;
if (Sync_AnimatedVisual_UAPv15.IsRuntimeCompatible())
{
var res =
new Sync_AnimatedVisual_UAPv15(
compositor
);
return res;
}
if (Sync_AnimatedVisual_UAPv7.IsRuntimeCompatible())
{
var res =
new Sync_AnimatedVisual_UAPv7(
compositor
);
return res;
}
return null;
}
/// <summary>
/// Gets the number of frames in the animation.
/// </summary>
public double FrameCount => 61d;
/// <summary>
/// Gets the frame rate of the animation.
/// </summary>
public double Framerate => 60d;
/// <summary>
/// Gets the duration of the animation.
/// </summary>
public TimeSpan Duration => TimeSpan.FromTicks(10166666);
/// <summary>
/// Converts a zero-based frame number to the corresponding progress value denoting the
/// start of the frame.
/// </summary>
public double FrameToProgress(double frameNumber)
{
return frameNumber / 61d;
}
/// <summary>
/// Returns a map from marker names to corresponding progress values.
/// </summary>
public IReadOnlyDictionary<string, double> Markers =>
new Dictionary<string, double>
{
};
/// <summary>
/// Sets the color property with the given name, or does nothing if no such property
/// exists.
/// </summary>
public void SetColorProperty(string propertyName, Color value)
{
}
/// <summary>
/// Sets the scalar property with the given name, or does nothing if no such property
/// exists.
/// </summary>
public void SetScalarProperty(string propertyName, double value)
{
}
sealed class Sync_AnimatedVisual_UAPv15
: Microsoft.UI.Xaml.Controls.IAnimatedVisual
{
const long c_durationTicks = 10166666;
readonly Compositor _c;
readonly ExpressionAnimation _reusableExpressionAnimation;
AnimationController _animationController_0;
CompositionColorBrush _colorBrush_AlmostDarkSlateGray_FF2D3846;
ContainerVisual _root;
void BindProperty(
CompositionObject target,
string animatedPropertyName,
string expression,
string referenceParameterName,
CompositionObject referencedObject)
{
_reusableExpressionAnimation.ClearAllParameters();
_reusableExpressionAnimation.Expression = expression;
_reusableExpressionAnimation.SetReferenceParameter(referenceParameterName, referencedObject);
target.StartAnimation(animatedPropertyName, _reusableExpressionAnimation);
}
ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(float initialProgress, float initialValue, CompositionEasingFunction initialEasingFunction)
{
var result = _c.CreateScalarKeyFrameAnimation();
result.Duration = TimeSpan.FromTicks(c_durationTicks);
result.InsertKeyFrame(initialProgress, initialValue, initialEasingFunction);
return result;
}
CompositionSpriteShape CreateSpriteShape(CompositionGeometry geometry, Matrix3x2 transformMatrix, CompositionBrush fillBrush)
{
var result = _c.CreateSpriteShape(geometry);
result.TransformMatrix = transformMatrix;
result.FillBrush = fillBrush;
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
AnimationController AnimationController_0()
{
var result = _animationController_0 = _c.CreateAnimationController();
result.Pause();
BindProperty(result, "Progress", "_.Progress", "_", _root);
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 2 Offset:<28.255, 18.903>
CanvasGeometry Geometry_0()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(11.7449999F, 5.09700012F));
builder.AddCubicBezier(new Vector2(11.7449999F, -3.66000009F), new Vector2(4.56699991F, -10.9189997F), new Vector2(-4.25500011F, -10.9189997F));
builder.AddCubicBezier(new Vector2(-8.6239996F, -10.9189997F), new Vector2(-12.7040005F, -9.20300007F), new Vector2(-15.7449999F, -6.08900023F));
builder.AddLine(new Vector2(-12.8739996F, -3.32599998F));
builder.AddCubicBezier(new Vector2(-10.5930004F, -5.66200018F), new Vector2(-7.53200006F, -6.94799995F), new Vector2(-4.25500011F, -6.94799995F));
builder.AddCubicBezier(new Vector2(2.36199999F, -6.94799995F), new Vector2(7.74499989F, -1.47099996F), new Vector2(7.74499989F, 5.09700012F));
builder.AddLine(new Vector2(3.74499989F, 5.09700012F));
builder.AddLine(new Vector2(9.74499989F, 10.9189997F));
builder.AddLine(new Vector2(15.7449999F, 5.09700012F));
builder.AddLine(new Vector2(11.7449999F, 5.09700012F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 1 Offset:<19.745, 29.096>
CanvasGeometry Geometry_1()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(4.25500011F, 6.94799995F));
builder.AddCubicBezier(new Vector2(-2.36199999F, 6.94799995F), new Vector2(-7.74499989F, 1.472F), new Vector2(-7.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-3.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-9.74499989F, -10.9189997F));
builder.AddLine(new Vector2(-15.7449999F, -5.09499979F));
builder.AddLine(new Vector2(-11.7449999F, -5.09499979F));
builder.AddCubicBezier(new Vector2(-11.7449999F, 3.66199994F), new Vector2(-4.56699991F, 10.9189997F), new Vector2(4.25500011F, 10.9189997F));
builder.AddCubicBezier(new Vector2(8.6260004F, 10.9189997F), new Vector2(12.7060003F, 9.20300007F), new Vector2(15.7449999F, 6.08900023F));
builder.AddLine(new Vector2(12.8739996F, 3.32500005F));
builder.AddCubicBezier(new Vector2(10.5930004F, 5.66099977F), new Vector2(7.53200006F, 6.94799995F), new Vector2(4.25500011F, 6.94799995F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
CompositionColorBrush ColorBrush_AlmostDarkSlateGray_FF2D3846()
{
return _colorBrush_AlmostDarkSlateGray_FF2D3846 = _c.CreateColorBrush(Color.FromArgb(0xFF, 0x2D, 0x38, 0x46));
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
CompositionContainerShape ContainerShape()
{
var result = _c.CreateContainerShape();
result.CenterPoint = new Vector2(24F, 24F);
var shapes = result.Shapes;
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
shapes.Add(SpriteShape_0());
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
shapes.Add(SpriteShape_1());
result.StartAnimation("RotationAngleInDegrees", RotationAngleInDegreesScalarAnimation_0_to_360(), AnimationController_0());
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
CompositionPathGeometry PathGeometry_0()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_0()));
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
CompositionPathGeometry PathGeometry_1()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_1()));
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_0()
{
// Offset:<28.255, 18.903>
var geometry = PathGeometry_0();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 28.2549992F, 18.9029999F), ColorBrush_AlmostDarkSlateGray_FF2D3846());;
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_1()
{
// Offset:<19.745, 29.096>
var geometry = PathGeometry_1();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 19.7450008F, 29.0960007F), _colorBrush_AlmostDarkSlateGray_FF2D3846);;
return result;
}
// The root of the composition.
ContainerVisual Root()
{
var result = _root = _c.CreateContainerVisual();
var propertySet = result.Properties;
propertySet.InsertScalar("Progress", 0F);
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
result.Children.InsertAtTop(ShapeVisual_0());
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Rotation
ScalarKeyFrameAnimation RotationAngleInDegreesScalarAnimation_0_to_360()
{
// Frame 0.
var result = CreateScalarKeyFrameAnimation(0F, 0F, HoldThenStepEasingFunction());
// Frame 61.
result.InsertKeyFrame(1F, 360F, _c.CreateCubicBezierEasingFunction(new Vector2(0.314999998F, 0F), new Vector2(0.465000004F, 0.861999989F)));
return result;
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
ShapeVisual ShapeVisual_0()
{
var result = _c.CreateShapeVisual();
result.Size = new Vector2(48F, 48F);
result.Shapes.Add(ContainerShape());
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// RotationAngleInDegrees
StepEasingFunction HoldThenStepEasingFunction()
{
var result = _c.CreateStepEasingFunction();
result.IsFinalStepSingleFrame = true;
return result;
}
internal Sync_AnimatedVisual_UAPv15(
Compositor compositor
)
{
_c = compositor;
_reusableExpressionAnimation = compositor.CreateExpressionAnimation();
Root();
}
public Visual RootVisual => _root;
public TimeSpan Duration => TimeSpan.FromTicks(c_durationTicks);
public Vector2 Size => new Vector2(48F, 48F);
void IDisposable.Dispose() => _root?.Dispose();
internal static bool IsRuntimeCompatible()
{
return Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 15);
}
}
sealed class Sync_AnimatedVisual_UAPv7
: Microsoft.UI.Xaml.Controls.IAnimatedVisual
{
const long c_durationTicks = 10166666;
readonly Compositor _c;
readonly ExpressionAnimation _reusableExpressionAnimation;
CompositionColorBrush _colorBrush_AlmostDarkSlateGray_FF2D3846;
ContainerVisual _root;
void BindProperty(
CompositionObject target,
string animatedPropertyName,
string expression,
string referenceParameterName,
CompositionObject referencedObject)
{
_reusableExpressionAnimation.ClearAllParameters();
_reusableExpressionAnimation.Expression = expression;
_reusableExpressionAnimation.SetReferenceParameter(referenceParameterName, referencedObject);
target.StartAnimation(animatedPropertyName, _reusableExpressionAnimation);
}
ScalarKeyFrameAnimation CreateScalarKeyFrameAnimation(float initialProgress, float initialValue, CompositionEasingFunction initialEasingFunction)
{
var result = _c.CreateScalarKeyFrameAnimation();
result.Duration = TimeSpan.FromTicks(c_durationTicks);
result.InsertKeyFrame(initialProgress, initialValue, initialEasingFunction);
return result;
}
CompositionSpriteShape CreateSpriteShape(CompositionGeometry geometry, Matrix3x2 transformMatrix, CompositionBrush fillBrush)
{
var result = _c.CreateSpriteShape(geometry);
result.TransformMatrix = transformMatrix;
result.FillBrush = fillBrush;
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 2 Offset:<28.255, 18.903>
CanvasGeometry Geometry_0()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(11.7449999F, 5.09700012F));
builder.AddCubicBezier(new Vector2(11.7449999F, -3.66000009F), new Vector2(4.56699991F, -10.9189997F), new Vector2(-4.25500011F, -10.9189997F));
builder.AddCubicBezier(new Vector2(-8.6239996F, -10.9189997F), new Vector2(-12.7040005F, -9.20300007F), new Vector2(-15.7449999F, -6.08900023F));
builder.AddLine(new Vector2(-12.8739996F, -3.32599998F));
builder.AddCubicBezier(new Vector2(-10.5930004F, -5.66200018F), new Vector2(-7.53200006F, -6.94799995F), new Vector2(-4.25500011F, -6.94799995F));
builder.AddCubicBezier(new Vector2(2.36199999F, -6.94799995F), new Vector2(7.74499989F, -1.47099996F), new Vector2(7.74499989F, 5.09700012F));
builder.AddLine(new Vector2(3.74499989F, 5.09700012F));
builder.AddLine(new Vector2(9.74499989F, 10.9189997F));
builder.AddLine(new Vector2(15.7449999F, 5.09700012F));
builder.AddLine(new Vector2(11.7449999F, 5.09700012F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
// - - - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// - - ShapeGroup: Group 1 Offset:<19.745, 29.096>
CanvasGeometry Geometry_1()
{
CanvasGeometry result;
using (var builder = new CanvasPathBuilder(null))
{
builder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding);
builder.BeginFigure(new Vector2(4.25500011F, 6.94799995F));
builder.AddCubicBezier(new Vector2(-2.36199999F, 6.94799995F), new Vector2(-7.74499989F, 1.472F), new Vector2(-7.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-3.74499989F, -5.09499979F));
builder.AddLine(new Vector2(-9.74499989F, -10.9189997F));
builder.AddLine(new Vector2(-15.7449999F, -5.09499979F));
builder.AddLine(new Vector2(-11.7449999F, -5.09499979F));
builder.AddCubicBezier(new Vector2(-11.7449999F, 3.66199994F), new Vector2(-4.56699991F, 10.9189997F), new Vector2(4.25500011F, 10.9189997F));
builder.AddCubicBezier(new Vector2(8.6260004F, 10.9189997F), new Vector2(12.7060003F, 9.20300007F), new Vector2(15.7449999F, 6.08900023F));
builder.AddLine(new Vector2(12.8739996F, 3.32500005F));
builder.AddCubicBezier(new Vector2(10.5930004F, 5.66099977F), new Vector2(7.53200006F, 6.94799995F), new Vector2(4.25500011F, 6.94799995F));
builder.EndFigure(CanvasFigureLoop.Closed);
result = CanvasGeometry.CreatePath(builder);
}
return result;
}
CompositionColorBrush ColorBrush_AlmostDarkSlateGray_FF2D3846()
{
return _colorBrush_AlmostDarkSlateGray_FF2D3846 = _c.CreateColorBrush(Color.FromArgb(0xFF, 0x2D, 0x38, 0x46));
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
CompositionContainerShape ContainerShape()
{
var result = _c.CreateContainerShape();
result.CenterPoint = new Vector2(24F, 24F);
var shapes = result.Shapes;
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
shapes.Add(SpriteShape_0());
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
shapes.Add(SpriteShape_1());
result.StartAnimation("RotationAngleInDegrees", RotationAngleInDegreesScalarAnimation_0_to_360());
var controller = result.TryGetAnimationController("RotationAngleInDegrees");
controller.Pause();
BindProperty(controller, "Progress", "_.Progress", "_", _root);
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 2 Offset:<28.255, 18.903>
CompositionPathGeometry PathGeometry_0()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_0()));
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// ShapeGroup: Group 1 Offset:<19.745, 29.096>
CompositionPathGeometry PathGeometry_1()
{
return _c.CreatePathGeometry(new CompositionPath(Geometry_1()));
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_0()
{
// Offset:<28.255, 18.903>
var geometry = PathGeometry_0();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 28.2549992F, 18.9029999F), ColorBrush_AlmostDarkSlateGray_FF2D3846());;
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Path 1
CompositionSpriteShape SpriteShape_1()
{
// Offset:<19.745, 29.096>
var geometry = PathGeometry_1();
var result = CreateSpriteShape(geometry, new Matrix3x2(1F, 0F, 0F, 1F, 19.7450008F, 29.0960007F), _colorBrush_AlmostDarkSlateGray_FF2D3846);;
return result;
}
// The root of the composition.
ContainerVisual Root()
{
var result = _root = _c.CreateContainerVisual();
var propertySet = result.Properties;
propertySet.InsertScalar("Progress", 0F);
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
result.Children.InsertAtTop(ShapeVisual_0());
return result;
}
// - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// Rotation
ScalarKeyFrameAnimation RotationAngleInDegreesScalarAnimation_0_to_360()
{
// Frame 0.
var result = CreateScalarKeyFrameAnimation(0F, 0F, HoldThenStepEasingFunction());
// Frame 61.
result.InsertKeyFrame(1F, 360F, _c.CreateCubicBezierEasingFunction(new Vector2(0.314999998F, 0F), new Vector2(0.465000004F, 0.861999989F)));
return result;
}
// Shape tree root for layer: main_library_shelf_icon_sync Outlines
ShapeVisual ShapeVisual_0()
{
var result = _c.CreateShapeVisual();
result.Size = new Vector2(48F, 48F);
result.Shapes.Add(ContainerShape());
return result;
}
// - - Shape tree root for layer: main_library_shelf_icon_sync Outlines
// RotationAngleInDegrees
StepEasingFunction HoldThenStepEasingFunction()
{
var result = _c.CreateStepEasingFunction();
result.IsFinalStepSingleFrame = true;
return result;
}
internal Sync_AnimatedVisual_UAPv7(
Compositor compositor
)
{
_c = compositor;
_reusableExpressionAnimation = compositor.CreateExpressionAnimation();
Root();
}
public Visual RootVisual => _root;
public TimeSpan Duration => TimeSpan.FromTicks(c_durationTicks);
public Vector2 Size => new Vector2(48F, 48F);
void IDisposable.Dispose() => _root?.Dispose();
internal static bool IsRuntimeCompatible()
{
return Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 7);
}
}
}
}
+147 -51
View File
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System;
using System.IO;
@@ -48,7 +49,8 @@ public partial class App : WinoApplication,
IRecipient<NewCalendarSynchronizationRequested>,
IRecipient<AccountCreatedMessage>,
IRecipient<AccountRemovedMessage>,
IRecipient<GetStartedFromWelcomeRequested>
IRecipient<GetStartedFromWelcomeRequested>,
IRecipient<WelcomeImportCompletedMessage>
{
private const int InboxSyncsPerFullSync = 20;
private const string ToggleDefaultModeLaunchArgument = "--mode=toggle-default";
@@ -63,7 +65,7 @@ public partial class App : WinoApplication,
private bool _isExiting;
private CancellationTokenSource? _autoSynchronizationLoopCts;
private readonly SemaphoreSlim _autoSynchronizationSemaphore = new(1, 1);
private readonly Dictionary<Guid, int> _inboxSyncCounters = [];
private readonly ConcurrentDictionary<Guid, int> _inboxSyncCounters = [];
private NativeTrayIcon? _trayIcon;
internal bool IsExiting => _isExiting;
@@ -756,7 +758,9 @@ public partial class App : WinoApplication,
/// Creates the main window without activating it.
/// Used for both normal launch and startup task launch (tray only).
/// </summary>
private void CreateWindow(Microsoft.UI.Xaml.LaunchActivatedEventArgs? args, string? forcedLaunchArguments = null)
private void CreateWindow(Microsoft.UI.Xaml.LaunchActivatedEventArgs? args,
string? forcedLaunchArguments = null,
ShellModeActivationContext? activationContextOverride = null)
{
LogActivation("Creating main window.");
@@ -769,14 +773,28 @@ public partial class App : WinoApplication,
windowManager.SetPrimaryNavigationFrame(WinoWindowKind.Shell, shellWindow.GetMainFrame());
var navigationService = Services.GetRequiredService<INavigationService>();
var defaultMode = _preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail;
var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
if (activationContextOverride != null)
{
var targetMode = !string.IsNullOrWhiteSpace(forcedLaunchArguments)
? AppModeActivationResolver.Resolve(forcedLaunchArguments, null, null, defaultMode)
: TryResolveActivationMode(activationArgs, defaultMode, out var resolvedActivationMode)
? resolvedActivationMode
: AppModeActivationResolver.Resolve(args?.Arguments, GetCurrentLaunchTileId(), Environment.CommandLine, defaultMode);
navigationService.ChangeApplicationMode(targetMode, activationContextOverride);
return;
}
if (!string.IsNullOrWhiteSpace(forcedLaunchArguments))
{
shellWindow.HandleAppActivation(forcedLaunchArguments);
return;
}
var activationArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
if (activationArgs.Kind == ExtendedActivationKind.Launch &&
activationArgs.Data is ILaunchActivatedEventArgs launchArgs)
{
@@ -791,7 +809,7 @@ public partial class App : WinoApplication,
return;
}
if (TryResolveActivationMode(activationArgs, _preferencesService?.DefaultApplicationMode ?? WinoApplicationMode.Mail, out var activationMode))
if (TryResolveActivationMode(activationArgs, defaultMode, out var activationMode))
{
shellWindow.HandleAppActivation(GetModeLaunchArgument(activationMode));
return;
@@ -859,6 +877,7 @@ public partial class App : WinoApplication,
WeakReferenceMessenger.Default.Register<AccountCreatedMessage>(this);
WeakReferenceMessenger.Default.Register<AccountRemovedMessage>(this);
WeakReferenceMessenger.Default.Register<GetStartedFromWelcomeRequested>(this);
WeakReferenceMessenger.Default.Register<WelcomeImportCompletedMessage>(this);
}
public async void Receive(NewMailSynchronizationRequested message)
@@ -882,6 +901,11 @@ public partial class App : WinoApplication,
syncResult.CompletedState,
message.Options.GroupedSynchronizationTrackingId));
if (syncResult.CompletedState is SynchronizationCompletedState.Success or SynchronizationCompletedState.PartiallyCompleted)
{
await ClearInvalidCredentialAttentionIfNeededAsync(message.Options.AccountId).ConfigureAwait(false);
}
if (syncResult.CompletedState == SynchronizationCompletedState.Failed ||
syncResult.CompletedState == SynchronizationCompletedState.PartiallyCompleted)
{
@@ -906,7 +930,12 @@ public partial class App : WinoApplication,
var dialogService = Services.GetRequiredService<IMailDialogService>();
dialogService.InfoBarMessage(
Translator.Info_SyncFailedTitle,
Translator.Exception_FailedToSynchronizeFolders,
message.Options.Type switch
{
CalendarSynchronizationType.CalendarMetadata => Translator.Exception_FailedToSynchronizeCalendarMetadata,
CalendarSynchronizationType.Strict => Translator.Exception_FailedToSynchronizeCalendarData,
_ => Translator.Exception_FailedToSynchronizeCalendarEvents
},
InfoBarMessageType.Error);
}
}
@@ -942,6 +971,47 @@ public partial class App : WinoApplication,
});
}
public void Receive(WelcomeImportCompletedMessage message)
{
_hasConfiguredAccounts = message.ImportedMailboxCount > 0;
var windowManager = Services.GetRequiredService<IWinoWindowManager>();
if (windowManager.GetWindow(WinoWindowKind.Welcome) == null)
return;
MainWindow?.DispatcherQueue?.TryEnqueue(async () =>
{
if (_preferencesService != null)
{
_preferencesService.PreferenceChanged -= PreferencesServiceChanged;
_preferencesService.PreferenceChanged += PreferencesServiceChanged;
}
CreateWindow(
null,
GetModeLaunchArgument(WinoApplicationMode.Mail),
new ShellModeActivationContext
{
SuppressStartupFlows = true
});
await LoadInitialWinoAccountAsync();
CloseWelcomeWindowIfPresent();
if (MainWindow != null)
{
await ActivateWindowAsync(MainWindow);
}
RestartAutoSynchronizationLoop();
Services.GetRequiredService<IMailDialogService>().InfoBarMessage(
Translator.GeneralTitle_Info,
Translator.WinoAccount_Management_ImportReloginReminder,
InfoBarMessageType.Information);
});
}
public void Receive(AccountRemovedMessage message)
{
var windowManager = Services.GetRequiredService<IWinoWindowManager>();
@@ -1078,52 +1148,16 @@ public partial class App : WinoApplication,
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
var currentAccountIds = accounts.Select(a => a.Id).ToHashSet();
_inboxSyncCounters.Keys.Where(a => !currentAccountIds.Contains(a)).ToList().ForEach(a => _inboxSyncCounters.Remove(a));
foreach (var account in accounts)
foreach (var staleAccountId in _inboxSyncCounters.Keys.Where(a => !currentAccountIds.Contains(a)).ToList())
{
cancellationToken.ThrowIfCancellationRequested();
if (_synchronizationManager.IsAccountSynchronizing(account.Id))
continue;
var inboxSyncOptions = new MailSynchronizationOptions()
{
AccountId = account.Id,
Type = MailSynchronizationType.InboxOnly
};
var inboxSyncResult = await _synchronizationManager.SynchronizeMailAsync(inboxSyncOptions, cancellationToken).ConfigureAwait(false);
if (inboxSyncResult.CompletedState is SynchronizationCompletedState.Success or SynchronizationCompletedState.PartiallyCompleted)
{
_inboxSyncCounters.TryAdd(account.Id, 0);
_inboxSyncCounters[account.Id]++;
if (_inboxSyncCounters[account.Id] >= InboxSyncsPerFullSync)
{
var fullSyncOptions = new MailSynchronizationOptions()
{
AccountId = account.Id,
Type = MailSynchronizationType.FullFolders
};
await _synchronizationManager.SynchronizeMailAsync(fullSyncOptions, cancellationToken).ConfigureAwait(false);
_inboxSyncCounters[account.Id] = 0;
}
}
if (!account.IsCalendarAccessGranted)
continue;
var calendarOptions = new CalendarSynchronizationOptions()
{
AccountId = account.Id,
Type = CalendarSynchronizationType.CalendarMetadata
};
await _synchronizationManager.SynchronizeCalendarAsync(calendarOptions, cancellationToken).ConfigureAwait(false);
_inboxSyncCounters.TryRemove(staleAccountId, out _);
}
var synchronizationTasks = accounts
.Select(account => ExecuteAutoSynchronizationForAccountAsync(account, cancellationToken))
.ToList();
await Task.WhenAll(synchronizationTasks).ConfigureAwait(false);
}
finally
{
@@ -1134,6 +1168,68 @@ public partial class App : WinoApplication,
}
}
private async Task ExecuteAutoSynchronizationForAccountAsync(Wino.Core.Domain.Entities.Shared.MailAccount account, CancellationToken cancellationToken)
{
if (_synchronizationManager == null)
return;
cancellationToken.ThrowIfCancellationRequested();
if (_synchronizationManager.IsAccountSynchronizing(account.Id))
return;
var inboxSyncOptions = new MailSynchronizationOptions
{
AccountId = account.Id,
Type = MailSynchronizationType.InboxOnly
};
var inboxSyncResult = await _synchronizationManager.SynchronizeMailAsync(inboxSyncOptions, cancellationToken).ConfigureAwait(false);
if (inboxSyncResult.CompletedState is SynchronizationCompletedState.Success or SynchronizationCompletedState.PartiallyCompleted)
{
await ClearInvalidCredentialAttentionIfNeededAsync(account.Id).ConfigureAwait(false);
var inboxSyncCount = _inboxSyncCounters.AddOrUpdate(account.Id, 1, (_, currentCount) => currentCount + 1);
if (inboxSyncCount >= InboxSyncsPerFullSync)
{
var fullSyncOptions = new MailSynchronizationOptions
{
AccountId = account.Id,
Type = MailSynchronizationType.FullFolders
};
await _synchronizationManager.SynchronizeMailAsync(fullSyncOptions, cancellationToken).ConfigureAwait(false);
_inboxSyncCounters[account.Id] = 0;
}
}
if (!account.IsCalendarAccessGranted)
return;
var calendarOptions = new CalendarSynchronizationOptions
{
AccountId = account.Id,
Type = CalendarSynchronizationType.CalendarMetadata
};
await _synchronizationManager.SynchronizeCalendarAsync(calendarOptions, cancellationToken).ConfigureAwait(false);
}
private async Task ClearInvalidCredentialAttentionIfNeededAsync(Guid accountId)
{
if (_accountService == null)
return;
var account = await _accountService.GetAccountAsync(accountId).ConfigureAwait(false);
if (account?.AttentionReason != AccountAttentionReason.InvalidCredentials)
return;
await _accountService.ClearAccountAttentionAsync(accountId).ConfigureAwait(false);
}
/// <summary>
/// Handles activation redirected from another instance (single-instancing).
/// This is called when a second instance tries to launch and redirects to this existing instance.
@@ -15,8 +15,9 @@
TargetType="Button">
<Setter Property="Margin" Value="0" />
<Setter Property="Height" Value="32" />
<Setter Property="Padding" Value="0" />
<Setter Property="Foreground" Value="{ThemeResource SystemColorControlAccentBrush}" />
<!--<Setter Property="Foreground" Value="{ThemeResource SystemColorControlAccentBrush}" />-->
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="VerticalAlignment" Value="Center" />
@@ -27,7 +28,7 @@
<Grid Margin="4,0,0,0" Background="Transparent">
<Grid Background="Transparent" ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="64" />
<ColumnDefinition Width="128" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
@@ -0,0 +1,14 @@
<UserControl
x:Class="Wino.Mail.WinUI.Controls.SyncAnimationControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animatedvisuals="using:Wino.Mail.WinUI.AnimatedVisuals">
<AnimatedVisualPlayer
x:Name="AnimationPlayer"
AutoPlay="True"
Stretch="Uniform">
<animatedvisuals:SyncRefreshAnimation />
</AnimatedVisualPlayer>
</UserControl>
@@ -0,0 +1,54 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Wino.Mail.WinUI.Controls;
public sealed partial class SyncAnimationControl : UserControl
{
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
nameof(IsPlaying),
typeof(bool),
typeof(SyncAnimationControl),
new PropertyMetadata(true, OnIsPlayingChanged));
public bool IsPlaying
{
get => (bool)GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, value);
}
public SyncAnimationControl()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (IsPlaying)
{
PlayAnimation();
}
}
private static void OnIsPlayingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (SyncAnimationControl)d;
if ((bool)e.NewValue)
{
control.PlayAnimation();
}
else
{
control.AnimationPlayer.Stop();
}
}
private void PlayAnimation()
{
#pragma warning disable CS4014 // Fire-and-forget is intentional for looped animation playback.
AnimationPlayer.PlayAsync(0, 1, looped: true);
#pragma warning restore CS4014
}
}
@@ -1,43 +0,0 @@
<ContentDialog
x:Class="Wino.Dialogs.WhatIsNewDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Wino.Mail.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Style="{StaticResource WinoDialogStyle}"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">480</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">560</x:Double>
<x:Double x:Key="ContentDialogMinHeight">480</x:Double>
<x:Double x:Key="ContentDialogMaxHeight">700</x:Double>
</ContentDialog.Resources>
<Grid RowSpacing="16">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<controls:UpdateNotesFlipViewControl x:Name="UpdateNotesControl" Sections="{x:Bind Sections, Mode=OneTime}" />
<StackPanel
Grid.Row="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="8">
<Button
x:Name="GetStartedButton"
Click="OnGetStartedClicked"
Content="{x:Bind domain:Translator.WhatIsNew_GetStartedButton}"
Style="{StaticResource AccentButtonStyle}"
Visibility="Collapsed" />
</StackPanel>
</Grid>
</ContentDialog>
@@ -1,54 +0,0 @@
using System.Collections.Generic;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Updates;
namespace Wino.Dialogs;
public sealed partial class WhatIsNewDialog : ContentDialog
{
private readonly IUpdateManager _updateManager;
public List<UpdateNoteSection> Sections { get; }
private bool _canClose = false;
public WhatIsNewDialog(UpdateNotes notes, IUpdateManager updateManager)
{
InitializeComponent();
_updateManager = updateManager;
Sections = notes.Sections;
// Show the Get Started button immediately when there is only one page.
UpdateNotesControl.SelectedIndexChanged += OnUpdateSectionChanged;
UpdateGetStartedButtonVisibility(UpdateNotesControl.SelectedIndex);
Closing += OnDialogClosing;
}
private void OnUpdateSectionChanged(object? sender, int selectedIndex)
=> UpdateGetStartedButtonVisibility(selectedIndex);
private void UpdateGetStartedButtonVisibility(int selectedIndex)
{
GetStartedButton.Visibility = selectedIndex == Sections.Count - 1
? Visibility.Visible
: Visibility.Collapsed;
}
private void OnDialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args)
{
// Only allow closing when Get Started button was clicked.
if (!_canClose)
args.Cancel = true;
}
private void OnGetStartedClicked(object sender, RoutedEventArgs e)
{
GetStartedButton.IsEnabled = false;
_updateManager.MarkUpdateNotesAsSeen();
_canClose = true;
Hide();
}
}
@@ -0,0 +1,76 @@
<ContentDialog
x:Class="Wino.Dialogs.WinoAccountSyncExportDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:domain="using:Wino.Core.Domain"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DefaultButton="Primary"
PrimaryButtonClick="ExportClicked"
PrimaryButtonStyle="{ThemeResource AccentButtonStyle}"
PrimaryButtonText="{x:Bind domain:Translator.Buttons_Export, Mode=OneTime}"
SecondaryButtonText="{x:Bind domain:Translator.Buttons_Close, Mode=OneTime}"
Style="{StaticResource WinoDialogStyle}"
Title="{x:Bind domain:Translator.WinoAccount_Management_ExportDialog_Title, Mode=OneTime}"
mc:Ignorable="d">
<ContentDialog.Resources>
<x:Double x:Key="ContentDialogMinWidth">520</x:Double>
<x:Double x:Key="ContentDialogMaxWidth">520</x:Double>
</ContentDialog.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Spacing="16">
<TextBlock
Text="{x:Bind domain:Translator.WinoAccount_Management_ExportDialog_Description, Mode=OneTime}"
TextWrapping="WrapWholeWords" />
<StackPanel Spacing="12">
<CheckBox
x:Name="PreferencesCheckBox"
Checked="SelectionChanged"
Content="{x:Bind domain:Translator.WinoAccount_Management_ExportDialog_IncludePreferences, Mode=OneTime}"
IsChecked="True"
Unchecked="SelectionChanged" />
<StackPanel Spacing="8">
<CheckBox
x:Name="AccountsCheckBox"
Checked="SelectionChanged"
Content="{x:Bind domain:Translator.WinoAccount_Management_ExportDialog_IncludeAccounts, Mode=OneTime}"
IsChecked="True"
Unchecked="SelectionChanged" />
<Border
Padding="12"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
CornerRadius="10">
<StackPanel Spacing="6">
<TextBlock
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind domain:Translator.WinoAccount_Management_ExportDialog_AccountsDisclaimer, Mode=OneTime}"
TextWrapping="WrapWholeWords" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind domain:Translator.WinoAccount_Management_ExportDialog_AccountsRelogin, Mode=OneTime}"
TextWrapping="WrapWholeWords" />
</StackPanel>
</Border>
</StackPanel>
</StackPanel>
<StackPanel
x:Name="ProgressPanel"
Spacing="8"
Visibility="Collapsed">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind domain:Translator.WinoAccount_Management_ExportDialog_InProgress, Mode=OneTime}"
TextWrapping="WrapWholeWords" />
<ProgressBar IsIndeterminate="True" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</ContentDialog>
@@ -0,0 +1,73 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
namespace Wino.Dialogs;
public sealed partial class WinoAccountSyncExportDialog : ContentDialog
{
private readonly IWinoAccountDataSyncService _syncService;
private bool _isBusy;
public WinoAccountSyncExportDialog(IWinoAccountDataSyncService syncService)
{
_syncService = syncService;
InitializeComponent();
UpdateButtonState();
}
public WinoAccountSyncExportResult? Result { get; private set; }
public Exception? FailureException { get; private set; }
private async void ExportClicked(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
args.Cancel = true;
if (!HasSelection())
{
return;
}
var deferral = args.GetDeferral();
try
{
SetBusyState(true);
FailureException = null;
Result = await _syncService.ExportAsync(new WinoAccountSyncSelection(
PreferencesCheckBox.IsChecked == true,
AccountsCheckBox.IsChecked == true));
Hide();
}
catch (Exception ex)
{
FailureException = ex;
Hide();
}
finally
{
SetBusyState(false);
deferral.Complete();
}
}
private void SelectionChanged(object sender, RoutedEventArgs e)
=> UpdateButtonState();
private void SetBusyState(bool isBusy)
{
_isBusy = isBusy;
ProgressPanel.Visibility = isBusy ? Visibility.Visible : Visibility.Collapsed;
IsSecondaryButtonEnabled = !isBusy;
UpdateButtonState();
}
private void UpdateButtonState()
=> IsPrimaryButtonEnabled = !_isBusy && HasSelection();
private bool HasSelection()
=> PreferencesCheckBox.IsChecked == true || AccountsCheckBox.IsChecked == true;
}
@@ -23,7 +23,9 @@ public partial class AccountCalendarStateService : ObservableRecipient,
IRecipient<CalendarListAdded>,
IRecipient<CalendarListUpdated>,
IRecipient<CalendarListDeleted>,
IRecipient<AccountRemovedMessage>
IRecipient<AccountRemovedMessage>,
IRecipient<AccountUpdatedMessage>,
IRecipient<AccountCalendarSynchronizationStateChanged>
{
private readonly object _calendarStateLock = new();
@@ -41,6 +43,9 @@ public partial class AccountCalendarStateService : ObservableRecipient,
[ObservableProperty]
public partial ReadOnlyObservableGroupedCollection<MailAccount, AccountCalendarViewModel> GroupedCalendars { get; set; }
[ObservableProperty]
public partial bool IsAnySynchronizationInProgress { get; set; }
public IEnumerable<AccountCalendarViewModel> ActiveCalendars
{
get
@@ -84,6 +89,8 @@ public partial class AccountCalendarStateService : ObservableRecipient,
Messenger.Register<CalendarListUpdated>(this);
Messenger.Register<CalendarListDeleted>(this);
Messenger.Register<AccountRemovedMessage>(this);
Messenger.Register<AccountUpdatedMessage>(this);
Messenger.Register<AccountCalendarSynchronizationStateChanged>(this);
}
private void SingleGroupCalendarCollectiveStateChanged(object? sender, EventArgs e)
@@ -114,6 +121,8 @@ public partial class AccountCalendarStateService : ObservableRecipient,
group.Add(calendar);
}
}
UpdateAggregateSynchronizationState();
}
}
@@ -140,6 +149,8 @@ public partial class AccountCalendarStateService : ObservableRecipient,
_internalGroupedCalendars.Remove(group);
}
}
UpdateAggregateSynchronizationState();
}
}
@@ -340,4 +351,60 @@ public partial class AccountCalendarStateService : ObservableRecipient,
}
}
}
public async void Receive(AccountUpdatedMessage message)
{
if (Dispatcher != null)
{
await Dispatcher.ExecuteOnUIThread(() => UpdateGroupedAccount(message.Account));
}
else
{
UpdateGroupedAccount(message.Account);
}
}
public async void Receive(AccountCalendarSynchronizationStateChanged message)
{
if (Dispatcher != null)
{
await Dispatcher.ExecuteOnUIThread(() => UpdateCalendarSynchronizationState(message));
}
else
{
UpdateCalendarSynchronizationState(message);
}
}
private void UpdateGroupedAccount(MailAccount updatedAccount)
{
GroupedAccountCalendarViewModel? groupedAccount;
lock (_calendarStateLock)
{
groupedAccount = _internalGroupedAccountCalendars.FirstOrDefault(a => a.Account.Id == updatedAccount.Id);
}
groupedAccount?.UpdateAccount(updatedAccount);
}
private void UpdateCalendarSynchronizationState(AccountCalendarSynchronizationStateChanged message)
{
GroupedAccountCalendarViewModel? groupedAccount;
lock (_calendarStateLock)
{
groupedAccount = _internalGroupedAccountCalendars.FirstOrDefault(a => a.Account.Id == message.AccountId);
}
if (groupedAccount == null)
return;
groupedAccount.IsSynchronizationInProgress = message.IsSynchronizationInProgress;
groupedAccount.SynchronizationStatus = message.SynchronizationStatus;
UpdateAggregateSynchronizationState();
}
private void UpdateAggregateSynchronizationState()
{
IsAnySynchronizationInProgress = _internalGroupedAccountCalendars.Any(a => a.IsSynchronizationInProgress);
}
}
+21 -2
View File
@@ -28,14 +28,16 @@ namespace Wino.Services;
public class DialogService : DialogServiceBase, IMailDialogService
{
private readonly IWinoAccountProfileService _winoAccountProfileService;
private readonly IWinoAccountDataSyncService _winoAccountDataSyncService;
public DialogService(INewThemeService themeService,
IConfigurationService configurationService,
IApplicationResourceManager<ResourceDictionary> applicationResourceManager,
IUpdateManager updateManager,
IWinoAccountProfileService winoAccountProfileService) : base(themeService, configurationService, applicationResourceManager, updateManager)
IWinoAccountProfileService winoAccountProfileService,
IWinoAccountDataSyncService winoAccountDataSyncService) : base(themeService, configurationService, applicationResourceManager)
{
_winoAccountProfileService = winoAccountProfileService;
_winoAccountDataSyncService = winoAccountDataSyncService;
}
public async Task<ICreateAccountAliasDialog> ShowCreateAccountAliasDialogAsync()
@@ -279,4 +281,21 @@ public class DialogService : DialogServiceBase, IMailDialogService
return dialog.Result;
}
public async Task<WinoAccountSyncExportResult?> ShowWinoAccountExportDialogAsync()
{
var dialog = new WinoAccountSyncExportDialog(_winoAccountDataSyncService)
{
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
await HandleDialogPresentationAsync(dialog);
if (dialog.FailureException != null)
{
throw dialog.FailureException;
}
return dialog.Result;
}
}
+1 -15
View File
@@ -16,7 +16,6 @@ using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Accounts;
using Wino.Core.Domain.Models.Common;
using Wino.Core.Domain.Models.Printing;
using Wino.Core.Domain.Models.Updates;
using Wino.Dialogs;
using Wino.Mail.WinUI.Dialogs;
using Wino.Mail.WinUI.Extensions;
@@ -31,16 +30,13 @@ public class DialogServiceBase : IDialogServiceBase
protected INewThemeService ThemeService { get; }
protected IConfigurationService ConfigurationService { get; }
protected IUpdateManager UpdateManager { get; }
protected IApplicationResourceManager<ResourceDictionary> ApplicationResourceManager { get; }
public DialogServiceBase(INewThemeService themeService, IConfigurationService configurationService, IApplicationResourceManager<ResourceDictionary> applicationResourceManager, IUpdateManager updateManager)
public DialogServiceBase(INewThemeService themeService, IConfigurationService configurationService, IApplicationResourceManager<ResourceDictionary> applicationResourceManager)
{
ThemeService = themeService;
ConfigurationService = configurationService;
ApplicationResourceManager = applicationResourceManager;
UpdateManager = updateManager;
}
protected XamlRoot? GetXamlRoot()
@@ -392,14 +388,4 @@ public class DialogServiceBase : IDialogServiceBase
return null!;
}
}
public async Task ShowWhatIsNewDialogAsync(UpdateNotes notes)
{
var dialog = new WhatIsNewDialog(notes, UpdateManager)
{
RequestedTheme = ThemeService.RootTheme.ToWindowsElementTheme()
};
await HandleDialogPresentationAsync(dialog);
}
}
+10 -3
View File
@@ -219,10 +219,13 @@ public class NavigationService : NavigationServiceBase, INavigationService
public bool ChangeApplicationMode(WinoApplicationMode mode)
=> ExecuteOnNavigationThread(() => ChangeApplicationModeInternal(mode));
public bool ChangeApplicationMode(WinoApplicationMode mode, ShellModeActivationContext activationContext)
=> ExecuteOnNavigationThread(() => ChangeApplicationModeInternal(mode, activationContext));
public bool CanGoBack()
=> ExecuteOnNavigationThread(CanGoBackInternal);
private bool ChangeApplicationModeInternal(WinoApplicationMode mode, object? activationParameter = null)
private bool ChangeApplicationModeInternal(WinoApplicationMode mode, ShellModeActivationContext? activationContext = null)
{
var coreFrame = GetCoreFrameInternal(NavigationReferenceFrame.ShellFrame);
@@ -254,7 +257,8 @@ public class NavigationService : NavigationServiceBase, INavigationService
shell.ActivateMode(mode, new ShellModeActivationContext
{
IsInitialActivation = isInitialShellNavigation,
Parameter = activationParameter
SuppressStartupFlows = activationContext?.SuppressStartupFlows ?? false,
Parameter = activationContext?.Parameter
});
ResetCurrentModeBackStackState();
@@ -280,7 +284,10 @@ public class NavigationService : NavigationServiceBase, INavigationService
{
if (_statePersistanceService.ApplicationMode != WinoApplicationMode.Settings)
{
return ChangeApplicationModeInternal(WinoApplicationMode.Settings, settingsTarget);
return ChangeApplicationModeInternal(WinoApplicationMode.Settings, new ShellModeActivationContext
{
Parameter = settingsTarget
});
}
page = WinoPage.SettingsPage;
File diff suppressed because one or more lines are too long
@@ -10,9 +10,9 @@
<ScrollViewer>
<StackPanel
MaxWidth="860"
Padding="36,28,36,36"
HorizontalAlignment="Center"
HorizontalAlignment="Stretch"
Spacing="24">
<!-- Page Header -->
@@ -219,6 +219,11 @@
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="Manage account settings" />
<TextBlock Margin="20,0,0,12" Text="Add new account or manage individidual account settings." />
<Button
Margin="20,0,0,12"
HorizontalAlignment="Left"
Click="ManageAccountsClicked"
Content="{x:Bind domain:Translator.MenuManageAccounts}" />
<ListView
x:Name="AccountsList"
IsItemClickEnabled="False"
@@ -51,6 +51,11 @@ public sealed partial class SettingOptionsPage : SettingOptionsPageAbstract
ViewModel.NavigateToAddAccount();
}
private void ManageAccountsClicked(object sender, RoutedEventArgs e)
{
ViewModel.NavigateToManageAccounts();
}
private void SettingsSearchTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput || string.IsNullOrWhiteSpace(sender.Text))
+28
View File
@@ -130,6 +130,34 @@
MaxWidth="600"
HorizontalAlignment="Center"
Spacing="8">
<HyperlinkButton
HorizontalAlignment="Center"
Command="{x:Bind ViewModel.ImportFromWinoAccountCommand}"
Content="{x:Bind domain:Translator.WelcomeWindow_ImportFromWinoAccount}" />
<StackPanel
x:Name="ImportProgressPanel"
HorizontalAlignment="Stretch"
Spacing="8"
Visibility="{x:Bind ViewModel.IsImportInProgress, Mode=OneWay}">
<TextBlock
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind domain:Translator.WelcomeWindow_ImportInProgress}" />
<ProgressBar IsIndeterminate="True" />
</StackPanel>
<TextBlock
x:Name="ImportStatusTextBlock"
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.ImportStatusMessage, Mode=OneWay}"
TextAlignment="Center"
TextWrapping="WrapWholeWords"
Visibility="{x:Bind ViewModel.HasImportStatus, Mode=OneWay}" />
<TextBlock
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
+60 -15
View File
@@ -92,17 +92,22 @@
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
</StackPanel>
<PathIcon
x:Name="AttentionIcon"
<Button
Grid.Column="1"
Width="16"
Height="16"
Margin="8,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
x:Load="{x:Bind IsAttentionRequired, Mode=OneWay}"
Data="F1 M 2.021484 18.769531 C 1.767578 18.769531 1.52832 18.720703 1.303711 18.623047 C 1.079102 18.525391 0.880534 18.391928 0.708008 18.222656 C 0.535482 18.053385 0.398763 17.856445 0.297852 17.631836 C 0.19694 17.407227 0.146484 17.167969 0.146484 16.914062 C 0.146484 16.614584 0.211589 16.328125 0.341797 16.054688 L 7.695312 1.347656 C 7.851562 1.035156 8.082682 0.784506 8.388672 0.595703 C 8.694661 0.406902 9.023438 0.3125 9.375 0.3125 C 9.726562 0.3125 10.055338 0.406902 10.361328 0.595703 C 10.667317 0.784506 10.898438 1.035156 11.054688 1.347656 L 18.408203 16.054688 C 18.53841 16.328125 18.603516 16.614584 18.603516 16.914062 C 18.603516 17.167969 18.553059 17.407227 18.452148 17.631836 C 18.351236 17.856445 18.216145 18.053385 18.046875 18.222656 C 17.877604 18.391928 17.679035 18.525391 17.451172 18.623047 C 17.223307 18.720703 16.982422 18.769531 16.728516 18.769531 Z M 16.728516 17.519531 C 16.884766 17.519531 17.027994 17.460938 17.158203 17.34375 C 17.28841 17.226562 17.353516 17.086588 17.353516 16.923828 C 17.353516 16.806641 17.330729 16.702475 17.285156 16.611328 L 9.931641 1.904297 C 9.879557 1.793621 9.80306 1.708984 9.702148 1.650391 C 9.601236 1.591797 9.492188 1.5625 9.375 1.5625 C 9.257812 1.5625 9.148763 1.593426 9.047852 1.655273 C 8.946939 1.717123 8.870442 1.800131 8.818359 1.904297 L 1.464844 16.611328 C 1.419271 16.702475 1.396484 16.803387 1.396484 16.914062 C 1.396484 17.083334 1.459961 17.226562 1.586914 17.34375 C 1.713867 17.460938 1.858724 17.519531 2.021484 17.519531 Z M 8.75 11.875 L 8.75 6.875 C 8.75 6.705729 8.811849 6.559245 8.935547 6.435547 C 9.059244 6.31185 9.205729 6.25 9.375 6.25 C 9.544271 6.25 9.690755 6.31185 9.814453 6.435547 C 9.93815 6.559245 10 6.705729 10 6.875 L 10 11.875 C 10 12.044271 9.93815 12.190756 9.814453 12.314453 C 9.690755 12.438151 9.544271 12.5 9.375 12.5 C 9.205729 12.5 9.059244 12.438151 8.935547 12.314453 C 8.811849 12.190756 8.75 12.044271 8.75 11.875 Z M 8.4375 14.375 C 8.4375 14.114584 8.528646 13.893229 8.710938 13.710938 C 8.893229 13.528646 9.114583 13.4375 9.375 13.4375 C 9.635416 13.4375 9.856771 13.528646 10.039062 13.710938 C 10.221354 13.893229 10.3125 14.114584 10.3125 14.375 C 10.3125 14.635417 10.221354 14.856771 10.039062 15.039062 C 9.856771 15.221354 9.635416 15.3125 9.375 15.3125 C 9.114583 15.3125 8.893229 15.221354 8.710938 15.039062 C 8.528646 14.856771 8.4375 14.635417 8.4375 14.375 Z "
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}" />
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource InfoBarWarningSeverityIconBackground}"
BorderThickness="1"
Click="AttentionIconClicked"
Foreground="{ThemeResource InfoBarWarningSeverityIconBackground}"
ToolTipService.ToolTip="{x:Bind domain:Translator.Info_AccountAttentionRequiredClickableMessage, Mode=OneWay}"
Visibility="{x:Bind IsAttentionRequired, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<PathIcon x:Name="AttentionIcon" Data="F1 M 2.021484 18.769531 C 1.767578 18.769531 1.52832 18.720703 1.303711 18.623047 C 1.079102 18.525391 0.880534 18.391928 0.708008 18.222656 C 0.535482 18.053385 0.398763 17.856445 0.297852 17.631836 C 0.19694 17.407227 0.146484 17.167969 0.146484 16.914062 C 0.146484 16.614584 0.211589 16.328125 0.341797 16.054688 L 7.695312 1.347656 C 7.851562 1.035156 8.082682 0.784506 8.388672 0.595703 C 8.694661 0.406902 9.023438 0.3125 9.375 0.3125 C 9.726562 0.3125 10.055338 0.406902 10.361328 0.595703 C 10.667317 0.784506 10.898438 1.035156 11.054688 1.347656 L 18.408203 16.054688 C 18.53841 16.328125 18.603516 16.614584 18.603516 16.914062 C 18.603516 17.167969 18.553059 17.407227 18.452148 17.631836 C 18.351236 17.856445 18.216145 18.053385 18.046875 18.222656 C 17.877604 18.391928 17.679035 18.525391 17.451172 18.623047 C 17.223307 18.720703 16.982422 18.769531 16.728516 18.769531 Z M 16.728516 17.519531 C 16.884766 17.519531 17.027994 17.460938 17.158203 17.34375 C 17.28841 17.226562 17.353516 17.086588 17.353516 16.923828 C 17.353516 16.806641 17.330729 16.702475 17.285156 16.611328 L 9.931641 1.904297 C 9.879557 1.793621 9.80306 1.708984 9.702148 1.650391 C 9.601236 1.591797 9.492188 1.5625 9.375 1.5625 C 9.257812 1.5625 9.148763 1.593426 9.047852 1.655273 C 8.946939 1.717123 8.870442 1.800131 8.818359 1.904297 L 1.464844 16.611328 C 1.419271 16.702475 1.396484 16.803387 1.396484 16.914062 C 1.396484 17.083334 1.459961 17.226562 1.586914 17.34375 C 1.713867 17.460938 1.858724 17.519531 2.021484 17.519531 Z M 8.75 11.875 L 8.75 6.875 C 8.75 6.705729 8.811849 6.559245 8.935547 6.435547 C 9.059244 6.31185 9.205729 6.25 9.375 6.25 C 9.544271 6.25 9.690755 6.31185 9.814453 6.435547 C 9.93815 6.559245 10 6.705729 10 6.875 L 10 11.875 C 10 12.044271 9.93815 12.190756 9.814453 12.314453 C 9.690755 12.438151 9.544271 12.5 9.375 12.5 C 9.205729 12.5 9.059244 12.438151 8.935547 12.314453 C 8.811849 12.190756 8.75 12.044271 8.75 11.875 Z M 8.4375 14.375 C 8.4375 14.114584 8.528646 13.893229 8.710938 13.710938 C 8.893229 13.528646 9.114583 13.4375 9.375 13.4375 C 9.635416 13.4375 9.856771 13.528646 10.039062 13.710938 C 10.221354 13.893229 10.3125 14.114584 10.3125 14.375 C 10.3125 14.635417 10.221354 14.856771 10.039062 15.039062 C 9.856771 15.221354 9.635416 15.3125 9.375 15.3125 C 9.114583 15.3125 8.893229 15.221354 8.710938 15.039062 C 8.528646 14.856771 8.4375 14.635417 8.4375 14.375 Z " />
<TextBlock Text="{x:Bind domain:Translator.Info_AccountAttentionRequiredAction, Mode=OneWay}" />
</StackPanel>
</Button>
<muxc:ProgressRing
x:Name="SynchronizationProgressBar"
@@ -455,6 +460,7 @@
Visibility="Collapsed">
<Grid x:Name="CalendarPaneContent" Visibility="Collapsed">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
@@ -481,9 +487,31 @@
Text="{x:Bind domain:Translator.CalendarEventCompose_NewEventButton, Mode=OneTime}" />
</coreControls:WinoNavigationViewItem>
<coreControls:WinoNavigationViewItem
x:Name="SynchronizeCalendarsNavigationItem"
Grid.Row="1"
Height="50"
Margin="0,0,0,12"
AutomationProperties.Name="{x:Bind domain:Translator.Buttons_Sync, Mode=OneTime}"
IsEnabled="{x:Bind ViewModel.CalendarClient.CanSynchronizeCalendars, Mode=OneWay}"
IsTabStop="True"
KeyDown="SynchronizeCalendarsNavigationItemKeyDown"
SelectsOnInvoked="False"
Tapped="SynchronizeCalendarsNavigationItemTapped">
<muxc:NavigationViewItem.Icon>
<coreControls:WinoFontIcon Icon="Sync" />
</muxc:NavigationViewItem.Icon>
<TextBlock
Margin="0,-2,0,0"
VerticalAlignment="Center"
FontSize="16"
Style="{StaticResource FlyoutPickerTitleTextBlockStyle}"
Text="{x:Bind domain:Translator.Buttons_Sync, Mode=OneTime}" />
</coreControls:WinoNavigationViewItem>
<muxc:CalendarView
x:Name="VisibleDateRangeCalendarView"
Grid.Row="1"
Grid.Row="2"
Margin="12,0"
HorizontalAlignment="Stretch"
SelectedDatesChanged="VisibleDateRangeCalendarViewSelectedDatesChanged"
@@ -492,7 +520,7 @@
<ListView
x:Name="CalendarHostListView"
Grid.Row="2"
Grid.Row="3"
SelectionMode="None">
<ListView.Header>
<TextBlock
@@ -509,6 +537,7 @@
<muxc:Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
@@ -519,13 +548,29 @@
IsChecked="{x:Bind IsCheckedState, Mode=TwoWay}"
IsThreeState="True" />
<TextBlock
<Ellipse
Grid.Column="1"
Width="12"
Height="12"
Margin="8,0,10,0"
VerticalAlignment="Center"
TextWrapping="Wrap">
<Run FontWeight="SemiBold" Text="{x:Bind Account.Name}" />
<Run FontSize="12" Text="(" /><Run FontSize="12" Text="{x:Bind Account.Address}" /><Run FontSize="12" Text=")" />
</TextBlock>
Fill="{x:Bind AccountColorHex, Converter={StaticResource HexToColorBrushConverter}, Mode=OneWay}" />
<StackPanel
Grid.Column="2"
VerticalAlignment="Center"
Spacing="2">
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap">
<Run FontWeight="SemiBold" Text="{x:Bind Account.Name}" />
<Run FontSize="12" Text=" (" /><Run FontSize="12" Text="{x:Bind Account.Address}" /><Run FontSize="12" Text=")" />
</TextBlock>
<ProgressBar
Height="4"
IsIndeterminate="True"
ShowPaused="False"
ShowError="False"
Visibility="{x:Bind IsSynchronizationProgressVisible, Mode=OneWay}" />
</StackPanel>
</Grid>
</muxc:Expander.Header>
<muxc:Expander.Content>
@@ -277,6 +277,17 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
await InvokeNewCalendarEventAsync();
}
private async void AttentionIconClicked(object sender, RoutedEventArgs e)
{
if (sender is not FrameworkElement { DataContext: AccountMenuItem accountMenuItem })
return;
if (ViewModel.MailClient is MailAppShellViewModel mailClient)
{
await mailClient.HandleAccountAttentionAsync(accountMenuItem.Parameter);
}
}
private async void NewCalendarEventNavigationItemKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key is not (VirtualKey.Enter or VirtualKey.Space))
@@ -289,6 +300,31 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
private Task InvokeNewCalendarEventAsync()
=> ViewModel.CalendarClient.HandleNavigationItemInvokedAsync(new NewCalendarEventMenuItem());
private async void SynchronizeCalendarsNavigationItemTapped(object sender, TappedRoutedEventArgs e)
{
e.Handled = true;
await InvokeCalendarSynchronizationAsync();
}
private async void SynchronizeCalendarsNavigationItemKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key is not (VirtualKey.Enter or VirtualKey.Space))
return;
e.Handled = true;
await InvokeCalendarSynchronizationAsync();
}
private Task InvokeCalendarSynchronizationAsync()
{
if (ViewModel.CalendarClient.SyncCommand.CanExecute(null))
{
ViewModel.CalendarClient.SyncCommand.Execute(null);
}
return Task.CompletedTask;
}
public void Receive(CalendarDisplayTypeChangedMessage message) => NotifyTitleBarContentChanged();
public void Receive(AccountCreatedMessage message)
+1
View File
@@ -200,6 +200,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.TabbedCommandBar" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.TokenizingTextBox" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
<PackageReference Include="CommunityToolkit.WinUI.Lottie" />
<PackageReference Include="Microsoft.Graphics.Win2D" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" />