feat(desktop): add configurable notifications
This commit is contained in:
parent
21af7395d8
commit
caf6f456c6
|
@ -1,33 +1,44 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Product Id="*" Name="XIVChat for Windows" Language="1033" Version="1.0.0.0" Manufacturer="XIVChat" UpgradeCode="82f30f39-99e8-4040-9bc0-32c1eac159df">
|
||||
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
|
||||
<Product Id="*" Name="XIVChat for Windows" Language="1033" Version="1.0.0.0" Manufacturer="XIVChat"
|
||||
UpgradeCode="82f30f39-99e8-4040-9bc0-32c1eac159df">
|
||||
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine"/>
|
||||
|
||||
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
|
||||
<MediaTemplate EmbedCab="yes"/>
|
||||
|
||||
<Feature Id="ProductFeature" Title="XIVChat for Windows" Level="1">
|
||||
<ComponentGroupRef Id="XIVChat_Desktop" />
|
||||
<ComponentRef Id="App_Start_Menu_Shortcut" />
|
||||
</Feature>
|
||||
</Product>
|
||||
<Feature Id="ProductFeature" Title="XIVChat for Windows" Level="1">
|
||||
<ComponentGroupRef Id="XIVChat_Desktop"/>
|
||||
<ComponentRef Id="App_Start_Menu_Shortcut"/>
|
||||
</Feature>
|
||||
</Product>
|
||||
|
||||
<Fragment>
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="XIVChat for Windows" />
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="ApplicationProgramsFolder" Name="XIVChat for Windows"/>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Fragment>
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="XIVChat for Windows"/>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="ApplicationProgramsFolder" Name="XIVChat for Windows"/>
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||
<Component Id="App_Start_Menu_Shortcut" Guid="6EF30C67-2EAA-4988-8470-E1149D5C1B50">
|
||||
<Shortcut Id="ApplicationStartMenuShortcut" Name="XIVChat for Windows" Target="[INSTALLFOLDER]XIVChat Desktop.exe" WorkingDirectory="INSTALLFOLDER" />
|
||||
<RemoveFolder Id="RemoveApplicationProgramsFolder" Directory="ApplicationProgramsFolder" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU" Key="Software\XIVChat for Windows" Name="installed" Type="integer" Value="1" KeyPath="yes" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
</Fragment>
|
||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||
<Component Id="App_Start_Menu_Shortcut" Guid="6EF30C67-2EAA-4988-8470-E1149D5C1B50">
|
||||
<Shortcut Id="ApplicationStartMenuShortcut" Name="XIVChat for Windows"
|
||||
Target="[INSTALLFOLDER]XIVChat Desktop.exe" WorkingDirectory="INSTALLFOLDER">
|
||||
<!-- AUMID -->
|
||||
<ShortcutProperty Key="System.AppUserModel.ID" Value="XIVChat.XIVChat_Desktop"/>
|
||||
|
||||
<!-- COM CLSID -->
|
||||
<ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID"
|
||||
Value="{F12BCC85-6FEE-4A9A-BBB8-08DFAA7BE1A8}"/>
|
||||
</Shortcut>
|
||||
<RemoveFolder Id="RemoveApplicationProgramsFolder" Directory="ApplicationProgramsFolder"
|
||||
On="uninstall"/>
|
||||
<RegistryValue Root="HKCU" Key="Software\XIVChat for Windows" Name="installed" Type="integer" Value="1"
|
||||
KeyPath="yes"/>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using Windows.UI.Notifications;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
using ModernWpf;
|
||||
using XIVChatCommon.Message;
|
||||
using XIVChatCommon.Message.Server;
|
||||
|
||||
// TODO: key word notification, notifications on message type, targeted message (like emote targeting you)
|
||||
// TODO: search messages
|
||||
// TODO: notifications for targeted messages (like emote targeting you)
|
||||
|
||||
namespace XIVChat_Desktop {
|
||||
/// <summary>
|
||||
|
@ -34,6 +40,8 @@ namespace XIVChat_Desktop {
|
|||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private async void Application_Startup(object sender, StartupEventArgs e) {
|
||||
Notifications.Initialise();
|
||||
|
||||
try {
|
||||
this.Config = Configuration.Load() ?? new Configuration();
|
||||
} catch (Exception ex) {
|
||||
|
@ -121,6 +129,7 @@ namespace XIVChat_Desktop {
|
|||
}
|
||||
|
||||
this.Connection = new Connection(this, host, port);
|
||||
this.Connection.ReceiveMessage += this.OnReceiveMessage;
|
||||
Task.Run(this.Connection.Connect);
|
||||
}
|
||||
|
||||
|
@ -132,5 +141,37 @@ namespace XIVChat_Desktop {
|
|||
this.Connection?.Disconnect();
|
||||
this.Connection = null;
|
||||
}
|
||||
|
||||
private void OnReceiveMessage(ServerMessage message) {
|
||||
if (!this.Config.Notifications.Any(notif => notif.Matches(message))) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sender = message.GetSenderPlayer();
|
||||
|
||||
var builder = new ToastContentBuilder();
|
||||
|
||||
if (sender != null) {
|
||||
var name = sender.Name;
|
||||
|
||||
if (sender.Server != 0) {
|
||||
name += $" ({Util.WorldName(sender.Server)})";
|
||||
}
|
||||
|
||||
builder.AddText(name);
|
||||
} else {
|
||||
builder.AddText("Notification");
|
||||
}
|
||||
|
||||
builder
|
||||
.AddText(message.ContentText)
|
||||
.AddAttributionText(message.Channel.Name());
|
||||
|
||||
var content = builder.GetToastContent();
|
||||
|
||||
var toast = new ToastNotification(content.GetXml());
|
||||
|
||||
DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,5 +84,41 @@
|
|||
</Button>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Header="Notifications">
|
||||
<Grid Margin="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ListView Grid.Column="0"
|
||||
x:Name="Notifications"
|
||||
ItemsSource="{Binding Config.Notifications}"
|
||||
MouseDoubleClick="Notifications_DoubleClick">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="Name"
|
||||
DisplayMemberBinding="{Binding Name}" />
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
Margin="8,0,0,0"
|
||||
Orientation="Vertical">
|
||||
<Button Margin="0,0,0,4"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="Add"
|
||||
Click="Notifications_Add_Click" />
|
||||
<Button Margin="0,0,0,4"
|
||||
HorizontalAlignment="Stretch"
|
||||
Content="Edit"
|
||||
Click="Notifications_Edit_Click" />
|
||||
<Button HorizontalAlignment="Stretch"
|
||||
Content="Delete"
|
||||
Click="Notifications_Delete_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</local:XivChatWindow>
|
||||
|
|
|
@ -49,5 +49,35 @@ namespace XIVChat_Desktop {
|
|||
var allDigits = e.Text.All(c => char.IsDigit(c));
|
||||
e.Handled = !allDigits;
|
||||
}
|
||||
|
||||
private void Notifications_DoubleClick(object sender, MouseButtonEventArgs e) {
|
||||
var context = ((FrameworkElement)e.OriginalSource).DataContext;
|
||||
if (!(context is Notification notification)) {
|
||||
return;
|
||||
}
|
||||
|
||||
new ManageNotification(this, notification).Show();
|
||||
}
|
||||
|
||||
private void Notifications_Add_Click(object sender, RoutedEventArgs e) {
|
||||
new ManageNotification(this, null).Show();
|
||||
}
|
||||
|
||||
private void Notifications_Edit_Click(object sender, RoutedEventArgs e) {
|
||||
if (!(this.Notifications.SelectedItem is Notification notif)) {
|
||||
return;
|
||||
}
|
||||
|
||||
new ManageNotification(this, notif).Show();
|
||||
}
|
||||
|
||||
private void Notifications_Delete_Click(object sender, RoutedEventArgs e) {
|
||||
if (!(this.Notifications.SelectedItem is Notification notif)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.Config.Notifications.Remove(notif);
|
||||
this.Config.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using XIVChatCommon.Message;
|
||||
using XIVChatCommon.Message.Server;
|
||||
|
||||
|
@ -73,6 +74,8 @@ namespace XIVChat_Desktop {
|
|||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<Notification> Notifications { get; set; } = new ObservableCollection<Notification>();
|
||||
|
||||
private void OnPropertyChanged(string propName) {
|
||||
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
|
||||
}
|
||||
|
@ -357,4 +360,70 @@ namespace XIVChat_Desktop {
|
|||
return this.Types.Any(type => type.Allowed(code));
|
||||
}
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class Notification {
|
||||
public string Name { get; set; }
|
||||
public List<ChatType> Channels { get; set; } = new List<ChatType>();
|
||||
public List<string> Substrings { get; set; } = new List<string>();
|
||||
|
||||
private IReadOnlyCollection<String> regexes = new List<string>();
|
||||
|
||||
public IReadOnlyCollection<string> Regexes {
|
||||
get => this.regexes;
|
||||
set {
|
||||
this.regexes = value;
|
||||
this.ResetRegexes();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public Lazy<List<Regex>> ParsedRegexes { get; private set; } = null!;
|
||||
|
||||
public Notification(string name) {
|
||||
this.Name = name;
|
||||
this.ResetRegexes();
|
||||
}
|
||||
|
||||
private void ResetRegexes() {
|
||||
this.ParsedRegexes = new Lazy<List<Regex>>(
|
||||
() => {
|
||||
try {
|
||||
return this.ParseRegexes();
|
||||
} catch (ArgumentException) {
|
||||
return new List<Regex>();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private List<Regex> ParseRegexes() {
|
||||
return this.Regexes
|
||||
.Select(regex => new Regex(regex, RegexOptions.Compiled))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
|
||||
public bool Matches(ServerMessage message) {
|
||||
if (!this.Channels.Contains(message.Channel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.Substrings.Count == 0 && this.Regexes.Count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var text = message.ContentText;
|
||||
|
||||
if (this.Substrings.Any(substring => text.ContainsIgnoreCase(substring))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.ParsedRegexes.Value.Any(regex => regex.IsMatch(text))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ namespace XIVChat_Desktop {
|
|||
|
||||
public readonly CancellationTokenSource cancel = new CancellationTokenSource();
|
||||
|
||||
public delegate void ReceiveMessageDelegate(ServerMessage message);
|
||||
|
||||
public event ReceiveMessageDelegate? ReceiveMessage;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
public string? CurrentChannel { get; private set; }
|
||||
|
||||
|
@ -269,6 +273,7 @@ namespace XIVChat_Desktop {
|
|||
var message = ServerMessage.Decode(payload);
|
||||
|
||||
this.app.Dispatch(() => {
|
||||
this.ReceiveMessage?.Invoke(message);
|
||||
this.app.Window.AddMessage(message);
|
||||
});
|
||||
break;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Windows;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using XIVChatCommon.Message.Server;
|
||||
|
||||
namespace XIVChat_Desktop.Controls {
|
||||
|
@ -54,11 +56,15 @@ namespace XIVChat_Desktop.Controls {
|
|||
}
|
||||
|
||||
var message = textBlock.Message;
|
||||
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var config = ((App)Application.Current).Config;
|
||||
if (config.Notifications.Any(notif => notif.Matches(message))) {
|
||||
textBlock.Background = new SolidColorBrush(Color.FromArgb(128, 200, 100, 100));
|
||||
}
|
||||
|
||||
textBlock.ClearValue(TextProperty);
|
||||
textBlock.Inlines.Clear();
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -165,6 +166,7 @@ namespace XIVChat_Desktop {
|
|||
this.Senders.Add(sender);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
|
||||
public bool AllowedMinusSenders(ServerMessage message) {
|
||||
if (!base.Allowed(message)) {
|
||||
return false;
|
||||
|
@ -181,6 +183,7 @@ namespace XIVChat_Desktop {
|
|||
return true;
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "ConvertIfStatementToReturnStatement")]
|
||||
public override bool Allowed(ServerMessage message) {
|
||||
if (!this.AllowedMinusSenders(message)) {
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<local:XivChatWindow x:Class="XIVChat_Desktop.ManageNotification"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:XIVChat_Desktop"
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
ui:WindowHelper.UseModernWindowStyle="True"
|
||||
Closed="ManageNotification_OnClosed"
|
||||
mc:Ignorable="d"
|
||||
Title="Manage notification"
|
||||
Height="800"
|
||||
Width="450"
|
||||
d:DataContext="{d:DesignInstance local:ManageNotification}">
|
||||
<local:XivChatWindow.CommandBindings>
|
||||
<CommandBinding Command="local:ManageNotification.AddEmpty"
|
||||
Executed="AddEmpty_Execute"
|
||||
CanExecute="AddEmpty_CanExecute" />
|
||||
</local:XivChatWindow.CommandBindings>
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Row="0"
|
||||
Margin="0,0,0,8"
|
||||
ui:ControlHelper.PlaceholderText="Name"
|
||||
ui:ControlHelper.Header="Name"
|
||||
Text="{Binding Notification.Name}" />
|
||||
|
||||
<Label Grid.Row="1"
|
||||
Margin="0,0,0,8"
|
||||
Content="Regular expressions" />
|
||||
|
||||
<ScrollViewer Height="100"
|
||||
Grid.Row="2">
|
||||
<ItemsControl ItemsSource="{Binding Regexes}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox Margin="0,0,0,8">
|
||||
<TextBox.Text>
|
||||
<Binding Path="Value">
|
||||
<Binding.ValidationRules>
|
||||
<local:RegexValidator />
|
||||
</Binding.ValidationRules>
|
||||
</Binding>
|
||||
</TextBox.Text>
|
||||
</TextBox>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<Button Grid.Row="3"
|
||||
Margin="0,0,0,8"
|
||||
Content="Add"
|
||||
Command="local:ManageNotification.AddEmpty"
|
||||
CommandParameter="{Binding Regexes}" />
|
||||
|
||||
<Label Grid.Row="4"
|
||||
Margin="0,0,0,8"
|
||||
Content="Substrings" />
|
||||
|
||||
<ScrollViewer Height="100"
|
||||
Grid.Row="5">
|
||||
<ItemsControl ItemsSource="{Binding Substrings}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox Margin="0,0,0,8"
|
||||
Text="{Binding Value}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<Button Grid.Row="6"
|
||||
Margin="0,0,0,8"
|
||||
Content="Add"
|
||||
Command="local:ManageNotification.AddEmpty"
|
||||
CommandParameter="{Binding Substrings}" />
|
||||
|
||||
<Label Grid.Row="7"
|
||||
Margin="0,0,0,8"
|
||||
Content="Channels" />
|
||||
|
||||
<ScrollViewer Grid.Row="8">
|
||||
<WrapPanel x:Name="Channels"
|
||||
Orientation="Vertical" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</local:XivChatWindow>
|
|
@ -0,0 +1,21 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
|
||||
namespace XIVChat_Desktop {
|
||||
public class Notifications {
|
||||
public static void Initialise() {
|
||||
DesktopNotificationManagerCompat.RegisterAumidAndComServer<XivChatNotificationActivator>("XIVChat.XIVChat_Desktop");
|
||||
DesktopNotificationManagerCompat.RegisterActivator<XivChatNotificationActivator>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces(typeof(INotificationActivationCallback))]
|
||||
[Guid("F12BCC85-6FEE-4A9A-BBB8-08DFAA7BE1A8"), ComVisible(true)]
|
||||
public class XivChatNotificationActivator : NotificationActivator {
|
||||
public override void OnActivated(string invokedArgs, NotificationUserInput userInput, string appUserModelId) {
|
||||
// TODO: Handle activation
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace XIVChat_Desktop {
|
||||
|
@ -673,5 +678,63 @@ namespace XIVChat_Desktop {
|
|||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
public static bool ContainsIgnoreCase(this string haystack, string needle) {
|
||||
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(haystack, needle, CompareOptions.IgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
public static bool IsValidRegex(this string regex) {
|
||||
try {
|
||||
_ = new Regex(regex);
|
||||
return true;
|
||||
} catch (ArgumentException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValidRegex(this string regex, out ArgumentException exception) {
|
||||
try {
|
||||
_ = new Regex(regex);
|
||||
exception = default!;
|
||||
return true;
|
||||
} catch (ArgumentException ex) {
|
||||
exception = ex;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RegexValidator : ValidationRule {
|
||||
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
|
||||
if (!(value is string text)) {
|
||||
return new ValidationResult(false, "Value is not text.");
|
||||
}
|
||||
|
||||
return text.IsValidRegex(out var ex)
|
||||
? ValidationResult.ValidResult
|
||||
: new ValidationResult(false, $"Invalid regular expression: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public class StringWrapper : INotifyPropertyChanged {
|
||||
private string value;
|
||||
|
||||
public String Value {
|
||||
get => this.value;
|
||||
set {
|
||||
this.value = value;
|
||||
this.OnPropertyChanged(nameof(this.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public StringWrapper(string value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string? propertyName = null) {
|
||||
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="6.1.1" />
|
||||
<PackageReference Include="ModernWpfUI" Version="0.9.2" />
|
||||
<PackageReference Include="ModernWpfUI.MahApps" Version="0.9.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
|
|
|
@ -538,6 +538,94 @@ namespace XIVChatCommon.Message {
|
|||
CrossLinkshell8 = 107,
|
||||
}
|
||||
|
||||
public static class ChatTypeExt {
|
||||
public static string? Name(this ChatType type) {
|
||||
return type switch {
|
||||
ChatType.Debug => "Debug",
|
||||
ChatType.Urgent => "Urgent",
|
||||
ChatType.Notice => "Notice",
|
||||
ChatType.Say => "Say",
|
||||
ChatType.Shout => "Shout",
|
||||
ChatType.TellOutgoing => "Tell (Outgoing)",
|
||||
ChatType.TellIncoming => "Tell (Incoming)",
|
||||
ChatType.Party => "Party",
|
||||
ChatType.Alliance => "Alliance",
|
||||
ChatType.Linkshell1 => "Linkshell [1]",
|
||||
ChatType.Linkshell2 => "Linkshell [2]",
|
||||
ChatType.Linkshell3 => "Linkshell [3]",
|
||||
ChatType.Linkshell4 => "Linkshell [4]",
|
||||
ChatType.Linkshell5 => "Linkshell [5]",
|
||||
ChatType.Linkshell6 => "Linkshell [6]",
|
||||
ChatType.Linkshell7 => "Linkshell [7]",
|
||||
ChatType.Linkshell8 => "Linkshell [8]",
|
||||
ChatType.FreeCompany => "Free Company",
|
||||
ChatType.NoviceNetwork => "Novice Network",
|
||||
ChatType.CustomEmote => "Custom Emotes",
|
||||
ChatType.StandardEmote => "Standard Emotes",
|
||||
ChatType.Yell => "Yell",
|
||||
ChatType.CrossParty => "Cross-world Party",
|
||||
ChatType.PvpTeam => "PvP Team",
|
||||
ChatType.CrossLinkshell1 => "Cross-world Linkshell [1]",
|
||||
ChatType.Damage => "Damage dealt",
|
||||
ChatType.Miss => "Failed attacks",
|
||||
ChatType.Action => "Actions used",
|
||||
ChatType.Item => "Items used",
|
||||
ChatType.Healing => "Healing",
|
||||
ChatType.GainBuff => "Beneficial effects granted",
|
||||
ChatType.GainDebuff => "Detrimental effects inflicted",
|
||||
ChatType.LoseBuff => "Beneficial effects lost",
|
||||
ChatType.LoseDebuff => "Detrimental effects cured",
|
||||
ChatType.Alarm => "Alarm Notifications",
|
||||
ChatType.Echo => "Echo",
|
||||
ChatType.System => "System Messages",
|
||||
ChatType.BattleSystem => "Battle System Messages",
|
||||
ChatType.GatheringSystem => "Gathering System Messages",
|
||||
ChatType.Error => "Error Messages",
|
||||
ChatType.NpcDialogue => "NPC Dialogue",
|
||||
ChatType.LootNotice => "Loot Notices",
|
||||
ChatType.Progress => "Progression Messages",
|
||||
ChatType.LootRoll => "Loot Messages",
|
||||
ChatType.Crafting => "Synthesis Messages",
|
||||
ChatType.Gathering => "Gathering Messages",
|
||||
ChatType.NpcAnnouncement => "NPC Dialogue (Announcements)",
|
||||
ChatType.FreeCompanyAnnouncement => "Free Company Announcements",
|
||||
ChatType.FreeCompanyLoginLogout => "Free Company Member Login Notifications",
|
||||
ChatType.RetainerSale => "Retainer Sale Notifications",
|
||||
ChatType.PeriodicRecruitmentNotification => "Periodic Recruitment Notifications",
|
||||
ChatType.Sign => "Sign Messages for PC Targets",
|
||||
ChatType.RandomNumber => "Random Number Messages",
|
||||
ChatType.NoviceNetworkSystem => "Novice Network Notifications",
|
||||
ChatType.Orchestrion => "Current Orchestrion Track Messages",
|
||||
ChatType.PvpTeamAnnouncement => "PvP Team Announcements",
|
||||
ChatType.PvpTeamLoginLogout => "PvP Team Member Login Notifications",
|
||||
ChatType.MessageBook => "Message Book Alert",
|
||||
ChatType.GmTell => "Tell (GM)",
|
||||
ChatType.GmSay => "Say (GM)",
|
||||
ChatType.GmShout => "Shout (GM)",
|
||||
ChatType.GmYell => "Yell (GM)",
|
||||
ChatType.GmParty => "Party (GM)",
|
||||
ChatType.GmFreeCompany => "Free Company (GM)",
|
||||
ChatType.GmLinkshell1 => "Linkshell [1] (GM)",
|
||||
ChatType.GmLinkshell2 => "Linkshell [2] (GM)",
|
||||
ChatType.GmLinkshell3 => "Linkshell [3] (GM)",
|
||||
ChatType.GmLinkshell4 => "Linkshell [4] (GM)",
|
||||
ChatType.GmLinkshell5 => "Linkshell [5] (GM)",
|
||||
ChatType.GmLinkshell6 => "Linkshell [6] (GM)",
|
||||
ChatType.GmLinkshell7 => "Linkshell [7] (GM)",
|
||||
ChatType.GmLinkshell8 => "Linkshell [8] (GM)",
|
||||
ChatType.GmNoviceNetwork => "Novice Network (GM)",
|
||||
ChatType.CrossLinkshell2 => "Cross-world Linkshell [2]",
|
||||
ChatType.CrossLinkshell3 => "Cross-world Linkshell [3]",
|
||||
ChatType.CrossLinkshell4 => "Cross-world Linkshell [4]",
|
||||
ChatType.CrossLinkshell5 => "Cross-world Linkshell [5]",
|
||||
ChatType.CrossLinkshell6 => "Cross-world Linkshell [6]",
|
||||
ChatType.CrossLinkshell7 => "Cross-world Linkshell [7]",
|
||||
ChatType.CrossLinkshell8 => "Cross-world Linkshell [8]",
|
||||
_ => type.ToString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")]
|
||||
public enum ChatSource : ushort {
|
||||
Self = 2,
|
||||
|
|
Loading…
Reference in New Issue