Skip to content

Commit faa9918

Browse files
committed
feat: sign out enabled, expand/collapse sequenced, animation improvements
1 parent be72f80 commit faa9918

File tree

10 files changed

+216
-68
lines changed

10 files changed

+216
-68
lines changed

App/Controls/ExpandContent.xaml

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,53 @@
1-
<?xml version="1.0" encoding="utf-8"?>
2-
31
<UserControl
42
x:Class="Coder.Desktop.App.Controls.ExpandContent"
53
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
64
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
7-
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
8-
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
9-
xmlns:toolkit="using:CommunityToolkit.WinUI"
10-
mc:Ignorable="d">
5+
xmlns:toolkit="using:CommunityToolkit.WinUI">
6+
7+
<Grid>
8+
<Grid x:Name="CollapsiblePanel"
9+
Visibility="Collapsed"
10+
Opacity="0"
11+
MaxHeight="0"
12+
toolkit:UIElementExtensions.ClipToBounds="True">
1113

12-
<Grid x:Name="CollapsiblePanel" Opacity="0" Visibility="Collapsed" toolkit:UIElementExtensions.ClipToBounds="True">
13-
<Grid.RenderTransform>
14-
<TranslateTransform x:Name="SlideTransform" Y="-10" />
15-
</Grid.RenderTransform>
14+
<Grid.RenderTransform>
15+
<TranslateTransform x:Name="Slide" Y="-16"/>
16+
</Grid.RenderTransform>
17+
</Grid>
1618

1719
<VisualStateManager.VisualStateGroups>
1820
<VisualStateGroup>
1921
<VisualState x:Name="ExpandedState">
20-
<Storyboard>
21-
<DoubleAnimation
22-
Storyboard.TargetName="CollapsiblePanel"
23-
Storyboard.TargetProperty="Opacity"
24-
To="1"
25-
Duration="0:0:0.2" />
26-
<DoubleAnimation
27-
Storyboard.TargetName="SlideTransform"
28-
Storyboard.TargetProperty="Y"
29-
To="0"
30-
Duration="0:0:0.2" />
22+
<Storyboard x:Name="ExpandSb">
23+
<DoubleAnimation Storyboard.TargetName="CollapsiblePanel"
24+
Storyboard.TargetProperty="MaxHeight"
25+
To="10000" Duration="0:0:0.12"
26+
EnableDependentAnimation="True"/>
27+
<DoubleAnimation Storyboard.TargetName="CollapsiblePanel"
28+
Storyboard.TargetProperty="Opacity"
29+
To="1" Duration="0:0:0.12"/>
30+
<DoubleAnimation Storyboard.TargetName="Slide"
31+
Storyboard.TargetProperty="Y"
32+
To="0" Duration="0:0:0.12"/>
3133
</Storyboard>
3234
</VisualState>
33-
3435
<VisualState x:Name="CollapsedState">
35-
<Storyboard Completed="{x:Bind CollapseAnimation_Completed}">
36-
<DoubleAnimation
37-
Storyboard.TargetName="CollapsiblePanel"
38-
Storyboard.TargetProperty="Opacity"
39-
To="0"
40-
Duration="0:0:0.2" />
41-
<DoubleAnimation
42-
Storyboard.TargetName="SlideTransform"
43-
Storyboard.TargetProperty="Y"
44-
To="-10"
45-
Duration="0:0:0.2" />
36+
<Storyboard x:Name="CollapseSb"
37+
Completed="{x:Bind CollapseStoryboard_Completed}">
38+
<DoubleAnimation Storyboard.TargetName="CollapsiblePanel"
39+
Storyboard.TargetProperty="MaxHeight"
40+
To="0" Duration="0:0:0.12"
41+
EnableDependentAnimation="True"/>
42+
<DoubleAnimation Storyboard.TargetName="CollapsiblePanel"
43+
Storyboard.TargetProperty="Opacity"
44+
To="0" Duration="0:0:0.12"/>
45+
<DoubleAnimation Storyboard.TargetName="Slide"
46+
Storyboard.TargetProperty="Y"
47+
To="-16" Duration="0:0:0.12"/>
4648
</Storyboard>
4749
</VisualState>
50+
4851
</VisualStateGroup>
4952
</VisualStateManager.VisualStateGroups>
5053
</Grid>

App/Controls/ExpandContent.xaml.cs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,101 @@
22
using Microsoft.UI.Xaml;
33
using Microsoft.UI.Xaml.Controls;
44
using Microsoft.UI.Xaml.Markup;
5+
using System;
6+
using System.Threading;
7+
using System.Threading.Tasks;
58

69
namespace Coder.Desktop.App.Controls;
710

11+
812
[ContentProperty(Name = nameof(Children))]
913
[DependencyProperty<bool>("IsOpen", DefaultValue = false)]
1014
public sealed partial class ExpandContent : UserControl
1115
{
1216
public UIElementCollection Children => CollapsiblePanel.Children;
1317

18+
private bool? _pendingIsOpen;
19+
20+
private static readonly SemaphoreSlim _sem = new(1, 1);
21+
22+
1423
public ExpandContent()
1524
{
1625
InitializeComponent();
26+
Loaded += (_, __) =>
27+
{
28+
if (_pendingIsOpen is bool v)
29+
{
30+
_ = AnimateAsync(v);
31+
_pendingIsOpen = null;
32+
}
33+
};
1734
}
1835

19-
public void CollapseAnimation_Completed(object? sender, object args)
36+
partial void OnIsOpenChanged(bool oldValue, bool newValue)
2037
{
21-
// Hide the panel completely when the collapse animation is done. This
22-
// cannot be done with keyframes for some reason.
23-
//
24-
// Without this, the space will still be reserved for the panel.
25-
CollapsiblePanel.Visibility = Visibility.Collapsed;
38+
if (!this.IsLoaded)
39+
{
40+
_pendingIsOpen = newValue;
41+
return;
42+
}
43+
_ = AnimateAsync(newValue);
2644
}
2745

28-
partial void OnIsOpenChanged(bool oldValue, bool newValue)
46+
private async Task AnimateAsync(bool open)
47+
{
48+
await _sem.WaitAsync();
49+
50+
try
51+
{
52+
if (open)
53+
{
54+
if (_currentlyOpen is not null && _currentlyOpen != this)
55+
await _currentlyOpen.StartCollapseAsync();
56+
57+
_currentlyOpen = this;
58+
CollapsiblePanel.Visibility = Visibility.Visible;
59+
60+
VisualStateManager.GoToState(this, "ExpandedState", true);
61+
await ExpandAsync(); // wait for your own expand
62+
}
63+
else
64+
{
65+
if (_currentlyOpen == this) _currentlyOpen = null;
66+
await StartCollapseAsync();
67+
}
68+
}
69+
finally
70+
{
71+
_sem.Release();
72+
}
73+
}
74+
75+
private static ExpandContent? _currentlyOpen;
76+
private TaskCompletionSource? _collapseTcs;
77+
78+
private async Task ExpandAsync()
2979
{
30-
var newState = newValue ? "ExpandedState" : "CollapsedState";
80+
CollapsiblePanel.Visibility = Visibility.Visible;
81+
VisualStateManager.GoToState(this, "ExpandedState", true);
82+
83+
var tcs = new TaskCompletionSource();
84+
void done(object? s, object e) { ExpandSb.Completed -= done; tcs.SetResult(); }
85+
ExpandSb.Completed += done;
86+
await tcs.Task;
87+
}
3188

32-
// The animation can't set visibility when starting or ending the
33-
// animation.
34-
if (newValue)
35-
CollapsiblePanel.Visibility = Visibility.Visible;
89+
private Task StartCollapseAsync()
90+
{
91+
_collapseTcs = new TaskCompletionSource();
92+
VisualStateManager.GoToState(this, "CollapsedState", true);
93+
return _collapseTcs.Task;
94+
}
3695

37-
VisualStateManager.GoToState(this, newState, true);
96+
private void CollapseStoryboard_Completed(object sender, object e)
97+
{
98+
CollapsiblePanel.Visibility = Visibility.Collapsed;
99+
_collapseTcs?.TrySetResult();
100+
_collapseTcs = null;
38101
}
39102
}

App/Services/RpcController.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ public async Task StopVpn(CancellationToken ct = default)
234234
MutateState(state => { state.VpnLifecycle = VpnLifecycle.Unknown; });
235235
throw new VpnLifecycleException($"Failed to stop VPN. Service reported failure: {reply.Stop.ErrorMessage}");
236236
}
237+
238+
if (reply.Stop.Success)
239+
{
240+
MutateState(state => { state.VpnLifecycle = VpnLifecycle.Stopped; });
241+
}
237242
}
238243

239244
public async ValueTask DisposeAsync()

App/ViewModels/AgentViewModel.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,20 @@ public AgentViewModel(ILogger<AgentViewModel> logger, ICoderApiClientFactory cod
237237

238238
Id = id;
239239

240-
PropertyChanged += (_, args) =>
240+
PropertyChanging += (x, args) =>
241241
{
242242
if (args.PropertyName == nameof(IsExpanded))
243243
{
244-
_expanderHost.HandleAgentExpanded(Id, IsExpanded);
244+
var value = !IsExpanded;
245+
if (value)
246+
_expanderHost.HandleAgentExpanded(Id, value);
247+
}
248+
};
245249

250+
PropertyChanged += (x, args) =>
251+
{
252+
if (args.PropertyName == nameof(IsExpanded))
253+
{
246254
// Every time the drawer is expanded, re-fetch all apps.
247255
if (IsExpanded && !FetchingApps)
248256
FetchApps();

App/ViewModels/TrayWindowLoginRequiredViewModel.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Coder.Desktop.App.Views;
33
using CommunityToolkit.Mvvm.Input;
44
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.UI.Xaml;
56

67
namespace Coder.Desktop.App.ViewModels;
78

@@ -31,4 +32,10 @@ public void Login()
3132
_signInWindow.Closed += (_, _) => _signInWindow = null;
3233
_signInWindow.Activate();
3334
}
35+
36+
[RelayCommand]
37+
public void Exit()
38+
{
39+
_ = ((App)Application.Current).ExitApplication();
40+
}
3441
}

App/ViewModels/TrayWindowViewModel.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public void HandleAgentExpanded(Uuid id, bool expanded)
126126
if (!expanded) return;
127127
_hasExpandedAgent = true;
128128
// Collapse every other agent.
129-
foreach (var otherAgent in Agents.Where(a => a.Id != id))
129+
foreach (var otherAgent in Agents.Where(a => a.Id != id && a.IsExpanded == true))
130130
otherAgent.SetExpanded(false);
131131
}
132132

@@ -360,11 +360,12 @@ private void ShowFileSyncListWindow()
360360
}
361361

362362
[RelayCommand]
363-
private void SignOut()
363+
private async Task SignOut()
364364
{
365-
if (VpnLifecycle is not VpnLifecycle.Stopped)
366-
return;
367-
_credentialManager.ClearCredentials();
365+
//if (VpnLifecycle is not VpnLifecycle.Stopped)
366+
// return;
367+
await _rpcController.StopVpn();
368+
await _credentialManager.ClearCredentials();
368369
}
369370

370371
[RelayCommand]

App/Views/Pages/TrayWindowLoginRequiredPage.xaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,14 @@
3434

3535
<TextBlock Text="Sign in" Foreground="{ThemeResource DefaultTextForegroundThemeBrush}" />
3636
</HyperlinkButton>
37+
38+
<HyperlinkButton
39+
Command="{x:Bind ViewModel.ExitCommand, Mode=OneWay}"
40+
Margin="-12,-8,-12,-5"
41+
HorizontalAlignment="Stretch"
42+
HorizontalContentAlignment="Left">
43+
44+
<TextBlock Text="Exit" Foreground="{ThemeResource DefaultTextForegroundThemeBrush}" />
45+
</HyperlinkButton>
3746
</StackPanel>
3847
</Page>

App/Views/Pages/TrayWindowMainPage.xaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@
333333

334334
<HyperlinkButton
335335
Command="{x:Bind ViewModel.SignOutCommand, Mode=OneWay}"
336-
IsEnabled="{x:Bind ViewModel.VpnLifecycle, Converter={StaticResource StoppedBoolConverter}, Mode=OneWay}"
337336
Margin="-12,0"
338337
HorizontalAlignment="Stretch"
339338
HorizontalContentAlignment="Left">

App/Views/TrayWindow.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,11 @@
2020

2121
<!-- This is where the current Page is displayed -->
2222
<controls:SizedFrame x:Name="RootFrame" />
23+
24+
<!-- proxy for animating resize -->
25+
<Border x:Name="SizeProxy"
26+
Width="0"
27+
Opacity="0"
28+
IsHitTestVisible="False" />
2329
</Grid>
2430
</Window>

0 commit comments

Comments
 (0)