Skip to content

Commit d184b0e

Browse files
Merge master into data-nodes-and-local-sync (#141)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent ef1c916 commit d184b0e

11 files changed

Lines changed: 977 additions & 6 deletions

File tree

src/ByteSync.Client/Business/Configurations/ApplicationSettings.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public ApplicationSettings()
2424
AgreesBetaWarning0 = false;
2525
TrustedPublicKeys = null;
2626
SettingsVersion = null;
27+
AcknowledgedAnnouncementIds = null;
2728
}
2829

2930
public string InstallationId { get; set; } = null!;
@@ -186,12 +187,39 @@ public ReadOnlyCollection<TrustedPublicKey>? DecodedTrustedPublicKeys
186187
}
187188

188189
public string? SettingsVersion { get; set; }
190+
191+
public string? AcknowledgedAnnouncementIds { get; set; }
189192

190193
private string EncryptionPassword { get; set; } = null!;
191194

192195
[XmlIgnore]
193196
public RSA PrivateRsa { get; private set; } = null!;
194197

198+
[XmlIgnore]
199+
public List<string> DecodedAcknowledgedAnnouncementIds
200+
{
201+
get
202+
{
203+
if (AcknowledgedAnnouncementIds.IsNullOrEmpty())
204+
{
205+
return new List<string>();
206+
}
207+
208+
return AcknowledgedAnnouncementIds!.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
209+
}
210+
set
211+
{
212+
if (value == null || value.Count == 0)
213+
{
214+
AcknowledgedAnnouncementIds = null;
215+
}
216+
else
217+
{
218+
AcknowledgedAnnouncementIds = string.Join(";", value);
219+
}
220+
}
221+
}
222+
195223
public object Clone()
196224
{
197225
return this.MemberwiseClone();
@@ -238,6 +266,27 @@ public void RemoveTrustedKey(TrustedPublicKey trustedPublicKey)
238266
TrustedPublicKeys = CryptographyUtils.Encrypt(json, EncryptionPassword);
239267
}
240268

269+
public void InitializeAcknowledgedAnnouncementIds()
270+
{
271+
AcknowledgedAnnouncementIds = null;
272+
}
273+
274+
public void AddAcknowledgedAnnouncementId(string announcementId)
275+
{
276+
var acknowledgedIds = DecodedAcknowledgedAnnouncementIds;
277+
278+
if (!acknowledgedIds.Contains(announcementId))
279+
{
280+
acknowledgedIds.Add(announcementId);
281+
DecodedAcknowledgedAnnouncementIds = acknowledgedIds;
282+
}
283+
}
284+
285+
public bool IsAnnouncementAcknowledged(string announcementId)
286+
{
287+
return DecodedAcknowledgedAnnouncementIds.Contains(announcementId);
288+
}
289+
241290
public void InitializeRsa()
242291
{
243292
var rsa = RSA.Create();

src/ByteSync.Client/ByteSync.Client.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,5 +227,9 @@
227227
<DependentUpon>SessionSettingsEditView.axaml</DependentUpon>
228228
<SubType>Code</SubType>
229229
</Compile>
230+
<Compile Update="Views\Announcements\AnnouncementView.axaml.cs">
231+
<DependentUpon>AnnouncementView.axaml</DependentUpon>
232+
<SubType>Code</SubType>
233+
</Compile>
230234
</ItemGroup>
231235
</Project>

src/ByteSync.Client/DependencyInjection/Modules/ViewModelsModule.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using ByteSync.Business.Navigations;
33
using ByteSync.Interfaces.Dialogs;
44
using ByteSync.ViewModels;
5+
using ByteSync.ViewModels.Announcements;
56
using ByteSync.ViewModels.Headers;
67
using ByteSync.ViewModels.Home;
78
using ByteSync.ViewModels.Lobbies;
@@ -28,6 +29,7 @@ protected override void Load(ContainerBuilder builder)
2829
.AsImplementedInterfaces();
2930

3031
builder.RegisterType<HeaderViewModel>().SingleInstance().AsSelf();
32+
builder.RegisterType<AnnouncementViewModel>().SingleInstance().AsSelf();
3133

3234
builder.RegisterType<HomeMainViewModel>().Keyed<IRoutableViewModel>(NavigationPanel.Home);
3335
builder.RegisterType<SessionMainViewModel>().Keyed<IRoutableViewModel>(NavigationPanel.CloudSynchronization);

src/ByteSync.Client/DependencyInjection/Modules/ViewsModule.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
using ByteSync.ViewModels.Home;
55
using ByteSync.ViewModels.Lobbies;
66
using ByteSync.ViewModels.Sessions;
7+
using ByteSync.ViewModels.Announcements;
78
using ByteSync.Views;
89
using ByteSync.Views.Home;
10+
using ByteSync.Views.Announcements;
911
using ByteSync.Views.Lobbies;
1012
using ByteSync.Views.Sessions;
1113
using ReactiveUI;
@@ -26,6 +28,7 @@ protected override void Load(ContainerBuilder builder)
2628
builder.RegisterType<HomeMainView>().As<IViewFor<HomeMainViewModel>>();
2729
builder.RegisterType<SessionMainView>().As<IViewFor<SessionMainViewModel>>();
2830
builder.RegisterType<LobbyMainView>().As<IViewFor<LobbyMainViewModel>>();
31+
builder.RegisterType<AnnouncementView>().As<IViewFor<AnnouncementViewModel>>();
2932

3033
builder.RegisterInstance(new AvaloniaActivationForViewFetcher())
3134
.As<IActivationForViewFetcher>()

src/ByteSync.Client/Services/Configurations/ApplicationSettingsRepository.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ApplicationSettingsRepository : IApplicationSettingsRepository
1818
private ProductSerialDescription? _productSerialDescription;
1919
private string? _encryptionPassword;
2020

21-
public const string APPLICATION_SETTINGS_LAST_FORMAT_VERSION = "1.5";
21+
public const string APPLICATION_SETTINGS_LAST_FORMAT_VERSION = "1.6";
2222

2323

2424
public ApplicationSettingsRepository(ILocalApplicationDataManager localApplicationDataManager,
@@ -173,6 +173,12 @@ private bool CheckFormatVersion(ApplicationSettings applicationSettings)
173173
applicationSettings.InitializeTrustedPublicKeys();
174174
}
175175

176+
// Initialize acknowledged announcement IDs if not present
177+
if (string.IsNullOrEmpty(applicationSettings.AcknowledgedAnnouncementIds))
178+
{
179+
applicationSettings.InitializeAcknowledgedAnnouncementIds();
180+
}
181+
176182
applicationSettings.SettingsVersion = APPLICATION_SETTINGS_LAST_FORMAT_VERSION;
177183

178184
needUpdate = true;
@@ -245,6 +251,7 @@ private ApplicationSettings PrepareApplicationSettings()
245251

246252
applicationSettings.InitializeRsa();
247253
applicationSettings.InitializeTrustedPublicKeys();
254+
applicationSettings.InitializeAcknowledgedAnnouncementIds();
248255

249256
CheckRsa(applicationSettings);
250257

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System.Collections.ObjectModel;
2+
using System.Reactive;
3+
using System.Reactive.Disposables;
4+
using ByteSync.Interfaces;
5+
using ByteSync.Interfaces.Repositories;
6+
using ReactiveUI;
7+
using ReactiveUI.Fody.Helpers;
8+
9+
namespace ByteSync.ViewModels.Announcements;
10+
11+
public class AnnouncementViewModel : ActivatableViewModelBase
12+
{
13+
private readonly IAnnouncementRepository _announcementRepository;
14+
private readonly ILocalizationService _localizationService;
15+
private readonly IApplicationSettingsRepository _applicationSettingsRepository;
16+
17+
public AnnouncementViewModel()
18+
{
19+
Announcements = new ObservableCollection<AnnouncementItemViewModel>();
20+
}
21+
22+
public AnnouncementViewModel(IAnnouncementRepository announcementRepository,
23+
ILocalizationService localizationService,
24+
IApplicationSettingsRepository applicationSettingsRepository) : this()
25+
{
26+
_announcementRepository = announcementRepository;
27+
_localizationService = localizationService;
28+
_applicationSettingsRepository = applicationSettingsRepository;
29+
30+
AcknowledgeAnnouncementCommand = ReactiveCommand.Create<string>(AcknowledgeAnnouncement);
31+
32+
this.WhenActivated(disposables =>
33+
{
34+
_announcementRepository.ObservableCache
35+
.Connect()
36+
.Subscribe(_ => Refresh())
37+
.DisposeWith(disposables);
38+
39+
_localizationService.CurrentCultureObservable
40+
.Subscribe(_ => Refresh())
41+
.DisposeWith(disposables);
42+
});
43+
}
44+
45+
public ObservableCollection<AnnouncementItemViewModel> Announcements { get; }
46+
47+
public ReactiveCommand<string, Unit> AcknowledgeAnnouncementCommand { get; }
48+
49+
[Reactive]
50+
public bool IsVisible { get; private set; }
51+
52+
private void Refresh()
53+
{
54+
var cultureCode = _localizationService.CurrentCultureDefinition.Code;
55+
var applicationSettings = _applicationSettingsRepository.GetCurrentApplicationSettings();
56+
var acknowledgedIds = applicationSettings.DecodedAcknowledgedAnnouncementIds;
57+
58+
var unacknowledgedAnnouncements = _announcementRepository.Elements
59+
.Where(a => !acknowledgedIds.Contains(a.Id))
60+
.Select(a => new AnnouncementItemViewModel
61+
{
62+
Id = a.Id,
63+
Message = a.Message.TryGetValue(cultureCode, out var msg)
64+
? msg
65+
: a.Message.Values.FirstOrDefault() ?? string.Empty
66+
})
67+
.ToList();
68+
69+
Announcements.Clear();
70+
foreach (var announcement in unacknowledgedAnnouncements)
71+
{
72+
Announcements.Add(announcement);
73+
}
74+
75+
IsVisible = Announcements.Count > 0;
76+
}
77+
78+
private void AcknowledgeAnnouncement(string announcementId)
79+
{
80+
_applicationSettingsRepository.UpdateCurrentApplicationSettings(settings =>
81+
{
82+
settings.AddAcknowledgedAnnouncementId(announcementId);
83+
}, true);
84+
85+
Refresh();
86+
}
87+
}
88+
89+
public class AnnouncementItemViewModel
90+
{
91+
public string Id { get; set; } = string.Empty;
92+
public string Message { get; set; } = string.Empty;
93+
}

src/ByteSync.Client/ViewModels/MainWindowViewModel.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using ByteSync.Interfaces.Repositories;
1313
using ByteSync.Interfaces.Services.Sessions;
1414
using ByteSync.Interfaces.Services.Sessions.Connecting;
15+
using ByteSync.ViewModels.Announcements;
1516
using ByteSync.ViewModels.Headers;
1617
using ByteSync.ViewModels.Misc;
1718
using ReactiveUI;
@@ -38,8 +39,9 @@ public MainWindowViewModel()
3839

3940
}
4041

41-
public MainWindowViewModel(ISessionService sessionService, ICloudSessionConnectionService cloudSessionConnectionService, INavigationService navigationService,
42-
IZoomService zoomService, FlyoutContainerViewModel? flyoutContainerViewModel, HeaderViewModel headerViewModel,
42+
public MainWindowViewModel(ISessionService sessionService, ICloudSessionConnectionService cloudSessionConnectionService, INavigationService navigationService,
43+
IZoomService zoomService, FlyoutContainerViewModel? flyoutContainerViewModel, HeaderViewModel headerViewModel,
44+
AnnouncementViewModel announcementViewModel,
4345
IIndex<NavigationPanel, IRoutableViewModel> navigationPanelViewModels, IMessageBoxViewModelFactory messageBoxViewModelFactory,
4446
IQuitSessionService quitSessionService, ICloudSessionConnectionRepository cloudSessionConnectionRepository,
4547
ILogger<MainWindowViewModel> logger)
@@ -58,9 +60,12 @@ public MainWindowViewModel(ISessionService sessionService, ICloudSessionConnecti
5860

5961
FlyoutContainer = flyoutContainerViewModel!;
6062
Header = headerViewModel;
63+
Announcement = announcementViewModel;
6164

6265
this.WhenActivated(disposables =>
6366
{
67+
announcementViewModel.Activator.Activate();
68+
6469
_zoomService.ZoomLevel
6570
.Select(zoomLevel => (1d / 100) * zoomLevel)
6671
.ToPropertyEx(this, x => x.ZoomLevel)
@@ -98,6 +103,9 @@ public MainWindowViewModel(ISessionService sessionService, ICloudSessionConnecti
98103
[Reactive]
99104
public ViewModelBase Header { get; set; }
100105

106+
[Reactive]
107+
public AnnouncementViewModel Announcement { get; set; }
108+
101109
[Reactive]
102110
public FlyoutContainerViewModel FlyoutContainer { get; set; }
103111

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<UserControl xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:announcements="clr-namespace:ByteSync.ViewModels.Announcements"
6+
xmlns:misc="clr-namespace:ByteSync.Views.Misc"
7+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="100"
8+
x:Class="ByteSync.Views.Announcements.AnnouncementView">
9+
<Design.DataContext>
10+
<announcements:AnnouncementViewModel />
11+
</Design.DataContext>
12+
13+
<Border Background="{DynamicResource OtherMemberBackGround}"
14+
BorderThickness="0 0 0 1" BorderBrush="{DynamicResource SystemBaseMediumLowColor}"
15+
IsVisible="{Binding IsVisible}" Padding="6">
16+
<ItemsControl ItemsSource="{Binding Announcements}">
17+
<ItemsControl.ItemTemplate>
18+
<DataTemplate>
19+
<Grid Margin="0 2">
20+
<Grid.ColumnDefinitions>
21+
<ColumnDefinition Width="*"/>
22+
<ColumnDefinition Width="Auto"/>
23+
</Grid.ColumnDefinitions>
24+
25+
<TextBlock Grid.Column="0"
26+
Text="{Binding Message}"
27+
TextWrapping="Wrap"
28+
HorizontalAlignment="Center"
29+
VerticalAlignment="Center"/>
30+
31+
<Button Grid.Column="1"
32+
Command="{Binding DataContext.AcknowledgeAnnouncementCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
33+
CommandParameter="{Binding Id}"
34+
HorizontalAlignment="Right"
35+
VerticalAlignment="Center"
36+
Margin="6 0 0 0"
37+
BorderThickness="0"
38+
Focusable="False"
39+
Cursor="Hand"
40+
Background="Transparent"
41+
CornerRadius="0">
42+
<misc:Icon FontSize="12" Value="RegularX" VerticalAlignment="Center" Margin="0 1 0 0" />
43+
</Button>
44+
</Grid>
45+
</DataTemplate>
46+
</ItemsControl.ItemTemplate>
47+
</ItemsControl>
48+
</Border>
49+
</UserControl>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Avalonia.ReactiveUI;
2+
using ByteSync.ViewModels.Announcements;
3+
using ReactiveUI;
4+
5+
namespace ByteSync.Views.Announcements;
6+
7+
public partial class AnnouncementView : ReactiveUserControl<AnnouncementViewModel>
8+
{
9+
public AnnouncementView()
10+
{
11+
InitializeComponent();
12+
this.WhenActivated(disposables => { });
13+
}
14+
}

src/ByteSync.Client/Views/MainWindow.axaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,19 @@
2727
<ScaleTransform ScaleX="{Binding ZoomLevel}" ScaleY="{Binding ZoomLevel}"></ScaleTransform>
2828
</LayoutTransformControl.LayoutTransform>
2929

30-
<Grid RowDefinitions="Auto,Auto,*" >
30+
<Grid RowDefinitions="Auto,Auto,Auto,*" >
3131

3232
<Border Grid.Row="0" x:Name="FocusSink"
3333
IsVisible="True"
3434
Focusable="True" />
3535

3636
<ContentControl Grid.Row="1" Content="{Binding Header}"/>
3737

38-
<rxui:RoutedViewHost PageTransition="{Binding PageTransition}" Grid.Row="2" Router="{Binding Router}" />
38+
<ContentControl Grid.Row="2" Content="{Binding Announcement}" IsVisible="{Binding Announcement.IsVisible}"/>
3939

40-
<ContentControl Grid.Row="2" Content="{Binding FlyoutContainer}" IsVisible="{Binding FlyoutContainer.IsFlyoutContainerVisible}"/>
40+
<rxui:RoutedViewHost PageTransition="{Binding PageTransition}" Grid.Row="3" Router="{Binding Router}" />
41+
42+
<ContentControl Grid.Row="3" Content="{Binding FlyoutContainer}" IsVisible="{Binding FlyoutContainer.IsFlyoutContainerVisible}"/>
4143
</Grid>
4244
</LayoutTransformControl>
4345
</Window>

0 commit comments

Comments
 (0)