Notification

This commit is contained in:
RMBGAME
2022-07-19 05:34:25 +08:00
parent 6571e6398c
commit ea712ab193
8 changed files with 177 additions and 260 deletions
@@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="500"
Width="600" Height="500"
Width="750" Height="500"
x:Class="System.Application.UI.Views.Windows.NoticeWindow"
xmlns:gif="using:AvaloniaGif"
xmlns:service="using:System.Application.Services"
@@ -19,103 +19,89 @@
x:CompileBindings="True">
<Panel HorizontalAlignment="Stretch">
<controls:TitleBar/>
<Border Margin="{StaticResource TitleBarAfterMargin}"
Padding="10">
<TabControl Classes="flat"
Items="{ReflectionBinding NoticeTypes,Source={x:Static service:NotificationService.Current}}"
SelectedItem="{ReflectionBinding Source={x:Static service:NotificationService.Current},Path=SelectGroup,Mode=TwoWay}"
Background="Transparent">
<TabControl.ItemTemplate>
<DataTemplate x:DataType="models:NoticeTypeDTO">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate x:DataType="vm:NoticeWindowViewModel">
<Border Classes="paper"
Background="{DynamicResource ThemeBrushKey}">
<Panel>
<StackPanel IsVisible="{ReflectionBinding Source={x:Static service:NotificationService.Current},Path=IsLoading,Mode=OneWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="10">
<ProgressBar
Width="100"
Height="100"
Classes="Circle"
IsIndeterminate="True"
Orientation="Vertical"
HorizontalAlignment="Center"/>
<TextBlock Text="{ReflectionBinding Path=Res.Loading,Mode=OneWay,Source={x:Static resx:R.Current}}"
HorizontalAlignment="Center"/>
</StackPanel>
<Panel Margin="{StaticResource TitleBarAfterMargin}">
<StackPanel IsVisible="{Binding Path=IsLoading,Mode=OneWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="10">
<ProgressBar
Width="100"
Height="100"
Classes="Circle"
IsIndeterminate="True"
Orientation="Vertical"
HorizontalAlignment="Center"/>
<TextBlock Text="{ReflectionBinding Path=Res.Loading,Mode=OneWay,Source={x:Static resx:R.Current}}"
HorizontalAlignment="Center"/>
</StackPanel>
<TextBlock Text="{ReflectionBinding Path=Res.NoticeEmpty,Mode=OneWay,Source={x:Static resx:R.Current}}"
IsVisible="{ReflectionBinding Source={x:Static service:NotificationService.Current},Path=IsEmpty,Mode=OneWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<TextBlock Text="{ReflectionBinding Path=Res.NoticeEmpty,Mode=OneWay,Source={x:Static resx:R.Current}}"
IsVisible="{Binding Path=IsEmpty,Mode=OneWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<Grid ColumnDefinitions="3*,Auto,7*">
<Border>
<ListBox Items="{ReflectionBinding SelectGroup.Items.DataSource,Source={x:Static service:NotificationService.Current}}"
SelectedItem="{ReflectionBinding NoticeItem,Source={x:Static service:NotificationService.Current},Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="models:NoticeDTO">
<StackPanel Margin="10" Spacing="8">
<TextBlock Text="{Binding Title,Mode=OneWay}"
Classes="h6"
TextTrimming="CharacterEllipsis"/>
<TextBlock
Text="{Binding Introduction}"
Classes="h8"
<Border Classes="paper"
Background="{DynamicResource ThemeBrushKey}"
IsVisible="{Binding Path=!IsEmpty,Mode=OneWay}">
<Panel>
<Grid ColumnDefinitions="3*,Auto,7*">
<Border>
<ListBox Items="{Binding Notices}"
SelectedItem="{Binding NoticeItem,Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="models:NoticeDTO">
<StackPanel Margin="10" Spacing="8">
<TextBlock Text="{Binding Title,Mode=OneWay}"
Classes="h6"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
<Separator Classes="Vertical"
Margin="5 0"
Grid.Column="1"/>
<ScrollViewer Padding="10"
Grid.Column="2">
<StackPanel IsVisible="{ReflectionBinding Source={x:Static service:NotificationService.Current},Path=NoticeItem.Title,Mode=OneWay,Converter={StaticResource IsNullConverter},ConverterParameter=invert}}">
<TextBlock Text="{ReflectionBinding NoticeItem.Title,Source={x:Static service:NotificationService.Current}}"
Classes="h3"
HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
<TextBlock
IsVisible="{ReflectionBinding Source={x:Static service:NotificationService.Current},Path=NoticeItem.Author,Mode=OneWay,Converter={StaticResource IsNullConverter},ConverterParameter=invert}}"
Text="{ReflectionBinding NoticeItem.Author,Source={x:Static service:NotificationService.Current},StringFormat={}{0}}"
VerticalAlignment="Center"/>
<TextBlock
Text="{ReflectionBinding NoticeItem.CreationTime,Source={x:Static service:NotificationService.Current},StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"
VerticalAlignment="Center"/>
</StackPanel>
<Separator Classes="Separator" Margin="0 10"/>
<gif:Image2 AutoStart="true"
Width="{ReflectionBinding $parent.Width}"
DecodeWidth="{ReflectionBinding $parent.Width}"
Margin="10 0"
Source="{ReflectionBinding NoticeItem.PictureStream^,Source={x:Static service:NotificationService.Current}}"
Stretch="Uniform" />
<TextBlock TextWrapping="Wrap"
Text="{ReflectionBinding NoticeItem.Introduction,Source={x:Static service:NotificationService.Current}}"
Classes="h6"/>
<TextBlock
Text="{Binding Introduction}"
Classes="h8"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Panel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Border>
<Separator Classes="Vertical"
Margin="5 0"
Grid.Column="1"/>
<ScrollViewer Padding="10"
Grid.Column="2">
<StackPanel IsVisible="{Binding NoticeItem.Title,Mode=OneWay,Converter={StaticResource IsNullConverter},ConverterParameter=invert}}">
<TextBlock Text="{Binding NoticeItem.Title}"
Classes="h3"
HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right">
<TextBlock
IsVisible="{Binding Path=NoticeItem.Author,Mode=OneWay,Converter={StaticResource IsNullConverter},ConverterParameter=invert}}"
Text="{Binding NoticeItem.Author}"
VerticalAlignment="Center"/>
<TextBlock
Text="{Binding NoticeItem.CreationTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"
VerticalAlignment="Center"/>
</StackPanel>
<Separator Classes="Separator" Margin="0 10"/>
<gif:Image2 AutoStart="true"
Width="{ReflectionBinding $parent.Width}"
DecodeWidth="{ReflectionBinding $parent.Width}"
Margin="10 0"
Source="{Binding NoticeItem.PictureStream^}"
Stretch="Uniform" />
<TextBlock TextWrapping="Wrap"
Text="{Binding NoticeItem.Introduction}"
Classes="h6"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Panel>
</Border>
</Panel>
</Panel>
</Window>
@@ -1,6 +1,7 @@
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System.Application.Models;
using System.Application.Properties;
using System.Application.Settings;
@@ -30,157 +31,82 @@ namespace System.Application.Services
readonly IHttpService httpService = DI.Get<IHttpService>();
public SourceList<NoticeTypeDTO> NoticeTypesSource { get; }
public SourceCache<NoticeDTO, Guid> NoticesSource { get; }
private ReadOnlyObservableCollection<NoticeTypeDTO> _NoticeTypes;
[Reactive]
public int UnreadNotificationsCount { get; set; }
public ReadOnlyObservableCollection<NoticeTypeDTO> NoticeTypes
{
get => _NoticeTypes;
set => this.RaiseAndSetIfChanged(ref _NoticeTypes, value);
}
bool _IsLoading;
public bool IsLoading
{
get => _IsLoading;
set => this.RaiseAndSetIfChanged(ref _IsLoading, value);
}
bool _IsEmpty;
public bool IsEmpty
{
get => _IsEmpty;
set => this.RaiseAndSetIfChanged(ref _IsEmpty, value);
}
private NoticeTypeDTO? _SelectGroup;
public NoticeTypeDTO? SelectGroup
{
get => _SelectGroup;
set => this.RaiseAndSetIfChanged(ref _SelectGroup, value);
}
NoticeDTO _NoticeItem;
public NoticeDTO NoticeItem
{
get => _NoticeItem;
set => this.RaiseAndSetIfChanged(ref _NoticeItem, value);
}
[Reactive]
public ObservableCollection<NoticeTypeDTO> NoticeTypes { get; set; }
public NotificationService()
{
mCurrent = this;
NoticeTypesSource = new SourceList<NoticeTypeDTO>();
this.WhenAnyValue(x => x.SelectGroup)
.Subscribe(async x =>
{
if (x != null)
{
IsLoading = true;
await GetTable(x);
IsLoading = false;
}
});
NoticeTypesSource
.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Sort(SortExpressionComparer<NoticeTypeDTO>.Ascending(x => x.Order).ThenBy(x => x.Name))
.Bind(out _NoticeTypes)
.Subscribe(_ =>
{
SelectGroup = NoticeTypesSource.Items.FirstOrDefault();
});
}
public async Task InitializeNotice()
{
IsLoading = true;
await GetTypes();
IsLoading = false;
NoticesSource = new SourceCache<NoticeDTO, Guid>(x => x.Id);
NoticeTypes = new ObservableCollection<NoticeTypeDTO>();
}
public async Task GetNews(int trycount = 0)
{
if (NoticeTypesSource.Count > 0)
var lastTime = await INotificationService.GetLastNotificationTime();
var client = ICloudServiceClient.Instance.Notice;
var result = await client.NewMsg(null, null);
if (result.IsSuccess && result.Content != null)
{
var lastTime = await INotificationService.GetLastNotificationTime();
var basics = NoticeTypesSource.Items.FirstOrDefault();
var client = ICloudServiceClient.Instance.Notice;
var result = await client.NewMsg(basics.Id, null);
if (result.IsSuccess && result.Content != null)
var noticeList = result.Content.Where(x => lastTime.HasValue ? x.EnableTime > lastTime : true);
if (noticeList.Count() > 1)
{
var noticeList = result.Content.Where(x => lastTime.HasValue ? x.EnableTime > lastTime : true);
if (noticeList.Count() > 1)
await INotificationService.SetLastNotificationTime(noticeList.Max(x => x.EnableTime));
INotificationService.Instance.Notify(new NotificationBuilder()
{
await INotificationService.SetLastNotificationTime(noticeList.Max(x => x.EnableTime));
INotificationService.Instance.Notify(new NotificationBuilder()
Title = AppResources.NotificationChannelType_Description_Announcement,
Content = AppResources.Notice_Tray.Format(result.Content.Length),
AutoCancel = NotificationBuilder.DefaultAutoCancel,
Type = NotificationType.Announcement,
Click = new NotificationBuilder.ClickAction(() =>
{
Title = AppResources.NotificationChannelType_Description_Announcement,
Content = AppResources.Notice_Tray.Format(result.Content.Length, basics.Name),
AutoCancel = NotificationBuilder.DefaultAutoCancel,
Type = NotificationType.Announcement,
Click = new NotificationBuilder.ClickAction(() =>
{
IWindowManager.Instance.Show(CustomWindow.Notice);
})
});
}
else
IWindowManager.Instance.Show(CustomWindow.Notice);
})
});
}
else
{
var notice = noticeList.FirstOrDefault();
if (notice != null && notice.EnableTime > lastTime)
{
var notice = noticeList.FirstOrDefault();
if (notice != null && notice.EnableTime > lastTime)
{
await INotificationService.SetLastNotificationTime(notice.EnableTime);
INotificationService.Instance.Notify(notice);
}
await INotificationService.SetLastNotificationTime(notice.EnableTime);
INotificationService.Instance.Notify(notice);
}
}
}
else if (trycount <= 2)
{
await GetTypes();
await GetNews(trycount++);
}
}
public async Task GetTypes()
public async Task LoadNoticeTypes()
{
var client = ICloudServiceClient.Instance.Notice;
var result = await client.Types();
if (result.IsSuccess)
{
NoticeTypesSource.Clear();
NoticeTypesSource.AddRange(result.Content!.OrderBy(x => x.Index));
NoticeTypes.Clear();
NoticeTypes.AddRange(result.Content!.OrderBy(x => x.Order));
}
}
public async Task GetTable(NoticeTypeDTO selectGroup)
public async Task LoadNotification(NoticeTypeDTO? selectGroup)
{
if (selectGroup != null)
var client = ICloudServiceClient.Instance.Notice;
var result = await client.Table(selectGroup?.Id, 0, 30);
if (result.IsSuccess)
{
var client = ICloudServiceClient.Instance.Notice;
var result = await client.Table(selectGroup.Id, selectGroup.Index);
if (result.IsSuccess)
foreach (var item in result.Content!.DataSource)
{
foreach (var item in result.Content!.DataSource)
{
if (!string.IsNullOrWhiteSpace(item.Picture))
item.PictureStream = httpService.GetImageAsync(item.Picture, ImageChannelType.NoticePicture);
}
SelectGroup!.Items = result.Content!;
NoticeItem = SelectGroup!.Items.DataSource.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(item.Picture))
item.PictureStream = httpService.GetImageAsync(item.Picture, ImageChannelType.NoticePicture);
}
IsEmpty = SelectGroup!.Items == null || SelectGroup.Items?.DataSource.Count == 0;
NoticesSource.Clear();
NoticesSource.AddOrUpdate(result.Content.DataSource);
}
}
}
@@ -76,15 +76,15 @@ namespace System.Application.UI.ViewModels
UserService.Current.ShowWindow(CustomWindow.BindPhoneNumber);
});
public override async void Activation()
{
if (IsFirstActivation && !NotificationService.Current.NoticeTypes.Any_Nullable())
await NotificationService.Current.InitializeNotice();
var defaultNotice = NotificationService.Current?.NoticeTypesSource.Items.FirstOrDefault();
if (defaultNotice != null && NotificationService.Current?.SelectGroup?.Id != defaultNotice.Id)
await NotificationService.Current!.GetTable(defaultNotice!);
base.Activation();
}
//public override async void Activation()
//{
// if (IsFirstActivation && !NotificationService.Current.NoticeTypes.Any_Nullable())
// await NotificationService.Current.InitializeNotice();
// var defaultNotice = NotificationService.Current?.NoticeTypesSource.Items.FirstOrDefault();
// if (defaultNotice != null && NotificationService.Current?.SelectGroup?.Id != defaultNotice.Id)
// await NotificationService.Current!.GetTable(defaultNotice!);
// base.Activation();
//}
public bool IsSponsor => UserService.Current.User?.UserType.HasFlag(UserType.Sponsor) ?? false;
@@ -1,6 +1,7 @@
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System.Application.Models;
using System.Application.Services;
using System.Application.UI.Resx;
@@ -17,10 +18,44 @@ namespace System.Application.UI.ViewModels
public ReactiveCommand<string, Unit>? OpenNotice { get; }
private ReadOnlyObservableCollection<NoticeDTO> _Notices;
public ReadOnlyObservableCollection<NoticeDTO> Notices => _Notices;
[Reactive]
public bool IsLoading { get; set; }
public bool IsEmpty => !Notices.Any_Nullable();
[Reactive]
public NoticeTypeDTO? SelectGroup { get; set; }
[Reactive]
public NoticeDTO NoticeItem { get; set; }
public NoticeWindowViewModel()
{
Title = GetTitleByDisplayName(DisplayName);
OpenNotice = ReactiveCommand.CreateFromTask<string>(x => Browser2.OpenAsync(x));
NotificationService.Current.NoticesSource
.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
.Sort(SortExpressionComparer<NoticeDTO>.Ascending(x => x.EnableTime).ThenBy(x => x.Title))
.Bind(out _Notices)
.Subscribe(_ =>
{
NoticeItem = Notices.FirstOrDefault();
this.RaisePropertyChanged(nameof(IsEmpty));
});
this.WhenValueChanged(x => x.SelectGroup, false)
.Subscribe(async x =>
{
IsLoading = true;
await NotificationService.Current.LoadNotification(x);
IsLoading = false;
});
}
public void OpenNoticeWeb(NoticeDTO item)
@@ -31,8 +66,9 @@ namespace System.Application.UI.ViewModels
public override async void Activation()
{
if (IsFirstActivation && NotificationService.Current.NoticeTypes.Count == 0)
await NotificationService.Current.InitializeNotice();
//if (IsFirstActivation)
await NotificationService.Current.LoadNoticeTypes();
await NotificationService.Current.LoadNotification(SelectGroup);
base.Activation();
}
@@ -38,36 +38,5 @@ namespace System.Application.Models
[N_JsonProperty("2")]
[S_JsonProperty("2")]
public int Order { get; set; }
#if MVVM_VM
/// <summary>
/// Table Index
/// </summary>
[MPIgnore]
[N_JsonIgnore]
[S_JsonIgnore]
public int Index
{
get => _Index;
set => this.RaiseAndSetIfChanged(ref _Index, value);
}
int _Index = 1;
/// <summary>
/// 通知列表
/// </summary>
[MPIgnore]
[N_JsonIgnore]
[S_JsonIgnore]
public PagedModel<NoticeDTO>? Items
{
get => mItems;
set => this.RaiseAndSetIfChanged(ref mItems, value);
}
PagedModel<NoticeDTO>? mItems;
#endif
}
}
@@ -8,8 +8,8 @@ namespace System.Application.Services.CloudService.Clients.Abstractions
{
Task<IApiResponse<NoticeTypeDTO[]>> Types();
Task<IApiResponse<PagedModel<NoticeDTO>>> Table(Guid typeId, int index, int? size = null);
Task<IApiResponse<PagedModel<NoticeDTO>>> Table(Guid? typeId, int index, int? size = null);
Task<IApiResponse<NoticeDTO[]>> NewMsg(Guid typeId, DateTimeOffset? time);
Task<IApiResponse<NoticeDTO[]>> NewMsg(Guid? typeId, DateTimeOffset? time);
}
}
@@ -21,22 +21,22 @@ namespace System.Application.Services.CloudService.Clients
requestUri: "api/Notice/Types",
cancellationToken: default);
public Task<IApiResponse<PagedModel<NoticeDTO>>> Table(Guid typeId, int index, int? size = null)
public Task<IApiResponse<PagedModel<NoticeDTO>>> Table(Guid? typeId, int index, int? size = null)
=> conn.SendAsync<PagedModel<NoticeDTO>>(
isPolly: true,
isAnonymous: true,
isSecurity: true,
method: HttpMethod.Post,
requestUri: $"api/Notice/List/{typeId}/{(int)DeviceInfo2.Platform()}/{(int)DeviceInfo2.Idiom()}/?index={index}{(size.HasValue ? $"&size={size}" : "")}",
requestUri: $"api/Notice/List/{(int)DeviceInfo2.Platform()}/{(int)DeviceInfo2.Idiom()}/?index={index}{(typeId.HasValue ? $"&typeid={typeId}" : "")}{(size.HasValue ? $"&size={size}" : "")}",
cancellationToken: default);
public Task<IApiResponse<NoticeDTO[]>> NewMsg(Guid typeId, DateTimeOffset? time)
public Task<IApiResponse<NoticeDTO[]>> NewMsg(Guid? typeId, DateTimeOffset? time)
=> conn.SendAsync<NoticeDTO[]>(
isPolly: true,
isAnonymous: true,
isSecurity: true,
method: HttpMethod.Get,
requestUri: $"api/Notice/NewMsg/{typeId}/{(int)DeviceInfo2.Platform()}/{(int)DeviceInfo2.Idiom()}{(time.HasValue ? $"?time={time}" : "")}",
requestUri: $"api/Notice/NewMsg/{(int)DeviceInfo2.Platform()}/{(int)DeviceInfo2.Idiom()}?v=1{(typeId.HasValue ? $"&typeid={typeId}" : "")}{(time.HasValue ? $"?time={time}" : "")}",
cancellationToken: default);
}
@@ -268,13 +268,13 @@ namespace System.Application.Services.CloudService
return ApiResponse.Ok(new NoticeTypeDTO[] { });
}
public async Task<IApiResponse<NoticeDTO[]>> NewMsg(Guid typeId, DateTimeOffset? time)
public async Task<IApiResponse<NoticeDTO[]>> NewMsg(Guid? typeId, DateTimeOffset? time)
{
await Task.Delay(1500);
return ApiResponse.Ok(new NoticeDTO[] { });
}
public async Task<IApiResponse<PagedModel<NoticeDTO>>> Table(Guid typeId, int index, int? size = null)
public async Task<IApiResponse<PagedModel<NoticeDTO>>> Table(Guid? typeId, int index, int? size = null)
{
await Task.Delay(1500);
return ApiResponse.Ok(new PagedModel<NoticeDTO> { });