Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Change Time from "American (AM/PM)" to "MilitaryTime/European (HH:mm)" option (#58) and DisplayAttribute for Enums in Settings (#61) #63

Merged
merged 2 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Aerochat/Attributes/DisplayAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// (iL - 21.12.2024) This is basically another abstraction layer for developers.
// Let's say you have a beautiful DropDown for the Settings, but your Enum Variable Names are pretty cryptic
// for the average End-User. With Aerochat.Attributes, you can show the user a beautiful string in the UI,
// while here in the Code you can keep your cryptic and scary sounding Names.

namespace Aerochat.Attributes
{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class DisplayAttribute : Attribute
{
public string Name { get; set; }

public DisplayAttribute(string name)
{
Name = name;
}
}
}
53 changes: 53 additions & 0 deletions Aerochat/Converter/EnumToStringConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Aerochat.Attributes;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace Aerochat.Windows
{
public class EnumToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return string.Empty; // Default empty string for no selection

if (value is Enum enumValue)
{
var fieldInfo = enumValue.GetType().GetField(enumValue.ToString());
var displayAttribute = fieldInfo?.GetCustomAttribute<DisplayAttribute>();
return displayAttribute?.Name ?? enumValue.ToString();
}

return value.ToString();
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Handle conversion back from the display name to enum
if (value == null || string.IsNullOrEmpty(value.ToString()))
return null;

try
{
var enumType = targetType;
var enumValue = Enum.GetValues(enumType)
.Cast<Enum>()
.FirstOrDefault(e => e.GetType()
.GetField(e.ToString())
?.GetCustomAttribute<DisplayAttribute>()?.Name == value.ToString());

return enumValue;
}
catch
{
return null;
}
}
}
}
33 changes: 33 additions & 0 deletions Aerochat/Converter/TimeFormatConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Aerochat.Enums;
using Aerochat.Settings;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace Aerochat.Windows
{
public class TimeFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DateTime dateTime)
{
// Retrieve SelectedTimeFormat from the SettingsManager
var format = SettingsManager.Instance.SelectedTimeFormat;
string formatString = format == TimeFormat.TwentyFourHour ? "HH:mm:ss" : "h:mm tt";

return dateTime.ToString(formatString);
}
return value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
18 changes: 18 additions & 0 deletions Aerochat/Enums/TimeFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Aerochat.Attributes;

namespace Aerochat.Enums
{
public enum TimeFormat
{
[Display("24-Hour Clock")]
TwentyFourHour,

[Display("12-Hour Clock (AM/PM)")]
TwelveHour
}
}
39 changes: 39 additions & 0 deletions Aerochat/Helpers/EnumHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Aerochat.Attributes;

namespace Aerochat.Helpers
{
public static class EnumHelper
{
public static string GetDisplayName(Enum enumValue)
{
var fieldInfo = enumValue.GetType()
.GetField(enumValue.ToString(), BindingFlags.Public | BindingFlags.Static);

if (fieldInfo != null)
{
var displayAttribute = fieldInfo.GetCustomAttribute<DisplayAttribute>();

if (displayAttribute != null)
{
return displayAttribute.Name;
}
}

return enumValue.ToString();
}

public static List<(string DisplayName, T EnumValue)> GetEnumDisplayList<T>() where T : Enum
{
return Enum.GetValues(typeof(T))
.Cast<T>()
.Select(e => (GetDisplayName(e), e)) // Create tuple of DisplayName and EnumValue
.ToList();
}
}
}
6 changes: 5 additions & 1 deletion Aerochat/Settings/SettingsManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Aerochat.ViewModels;
using Aerochat.Enums;
using Aerochat.ViewModels;
using System;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -103,6 +104,9 @@ public static void Load()
[Settings("Appearance", "Show community-submitted ads on the home page")]

public bool DisplayAds { get; set; } = true;

[Settings("Appearance", "Time format")]
public TimeFormat SelectedTimeFormat { get; set; } = TimeFormat.TwentyFourHour;
#endregion
}
}
5 changes: 5 additions & 0 deletions Aerochat/ViewModels/Base.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string? propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
Expand Down
48 changes: 41 additions & 7 deletions Aerochat/ViewModels/Message.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using Aerochat.Hoarder;
using Aerochat.Enums;
using Aerochat.Hoarder;
using Aerochat.Settings;
using DSharpPlus;
using DSharpPlus.Entities;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand All @@ -16,7 +20,6 @@ public class MessageViewModel : ViewModelBase
private UserViewModel? _author;
private string _message;
private string _rawMessage;
private DateTime _timestamp;
private ulong? _id;
private bool _ephemeral = false;
private bool _special = false;
Expand All @@ -43,11 +46,6 @@ public string RawMessage
get => _rawMessage;
set => SetProperty(ref _rawMessage, value);
}
public DateTime Timestamp
{
get => _timestamp;
set => SetProperty(ref _timestamp, value);
}
public ulong? Id
{
get => _id;
Expand Down Expand Up @@ -94,6 +92,42 @@ public DiscordMessage MessageEntity
set => SetProperty(ref _messageEntity, value);
}

public string TimestampString
{
get
{
var format = SettingsManager.Instance.SelectedTimeFormat == TimeFormat.TwentyFourHour ? "HH:mm" : "h:mm tt";
return Timestamp.ToString(format, CultureInfo.InvariantCulture); // Ensure InvariantCulture to control formatting: Otherwise we will lose the AM/PM at the end!
}
}

private DateTime _timestamp;
public DateTime Timestamp
{
get { return _timestamp; }
set
{
if (_timestamp != value)
{
_timestamp = value;
RaisePropertyChanged(nameof(Timestamp));
RaisePropertyChanged(nameof(TimestampString));
}
}
}

public virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public event PropertyChangedEventHandler PropertyChanged;

public void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public ObservableCollection<AttachmentViewModel> Attachments { get; } = new();
public ObservableCollection<EmbedViewModel> Embeds { get; } = new();

Expand Down
20 changes: 19 additions & 1 deletion Aerochat/ViewModels/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using Aerochat.Enums;
using Aerochat.Helpers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Vanara.PInvoke.ShlwApi;

namespace Aerochat.ViewModels
{
Expand All @@ -13,6 +16,10 @@ public class SettingViewModel : ViewModelBase
private string _name;
private string _defaultValue;

public ObservableCollection<string> EnumValues { get; set; } = new ObservableCollection<string>();
public ObservableCollection<string> TimeFormatOptions { get; set; }
public TimeFormat SelectedTimeFormat { get; set; }

public string Type
{
get => _type;
Expand All @@ -28,6 +35,17 @@ public string DefaultValue
get => _defaultValue;
set => SetProperty(ref _defaultValue, value);
}

private string _selectedEnumValue;
public string SelectedEnumValue
{
get => _selectedEnumValue;
set
{
_selectedEnumValue = value;
OnPropertyChanged();
}
}
}

public class SettingsCategory : ViewModelBase
Expand Down
9 changes: 7 additions & 2 deletions Aerochat/Windows/Chat.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@
</Style>
</Window.Style>
<Window.Resources>
<Storyboard x:Key="FadeOutStoryboard">
<local:TimeFormatConverter x:Key="TimeFormatConverter"/>

<Storyboard x:Key="FadeOutStoryboard">
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="0"
Duration="0:0:0.3"
Expand Down Expand Up @@ -909,7 +911,10 @@
</Image.Style>
</Image>
<TextBlock FontSize="13" Grid.Row="0" Text=" said (" Foreground="#525252" />
<TextBlock FontSize="13" Grid.Row="0" Text="{Binding Timestamp, StringFormat=HH:mm}" Foreground="#525252" />
<!--<TextBlock FontSize="13" Grid.Row="0" Text="{Binding Timestamp, StringFormat=HH:mm:ss}" Foreground="#525252" />-->
<TextBlock FontSize="13" Foreground="#525252">
<TextBlock FontSize="13" Grid.Row="0" Text="{Binding TimestampString}" Foreground="#525252" />
</TextBlock>
<TextBlock FontSize="13" Grid.Row="0" Text="):" Foreground="#525252" />
</StackPanel>
</StackPanel>
Expand Down
32 changes: 32 additions & 0 deletions Aerochat/Windows/Chat.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
using Timer = System.Timers.Timer;
using DSharpPlus.Exceptions;
using static Aerochat.Windows.ToolbarItem;
using Aerochat.Enums;

namespace Aerochat.Windows
{
Expand Down Expand Up @@ -731,6 +732,10 @@ public Chat(ulong id, bool allowDefault = false)
};
ViewModel.Messages.CollectionChanged += UpdateHiddenInfo;
TypingUsers.CollectionChanged += TypingUsers_CollectionChanged;

// (iL - 20.12.2024) Subscribe to settings changes for live update
SettingsManager.Instance.PropertyChanged += OnSettingsChanged;

Closing += Chat_Closing;
Loaded += Chat_Loaded;
Discord.Client.TypingStarted += OnType;
Expand All @@ -745,6 +750,33 @@ public Chat(ulong id, bool allowDefault = false)
DrawingCanvas.Strokes.StrokesChanged += Strokes_StrokesChanged;
}

private void OnSettingsChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SettingsManager.Instance.SelectedTimeFormat))
{
Dispatcher.Invoke(() =>
{
foreach (var message in ViewModel.Messages)
{
// Update each message
message.RaisePropertyChanged(nameof(MessageViewModel.TimestampString));
}

// Force the collection to refresh
// (iL - 21.12.2024) I know that this is a really shitty way to force the UI to update,
// but I wasn't able to implement the live updating any other way after
// fooling around with it for an hour.
// Maybe you have a better idea? :-)
var tempMessages = ViewModel.Messages.ToList();
ViewModel.Messages.Clear();
foreach (var msg in tempMessages)
{
ViewModel.Messages.Add(msg);
}
});
}
}

private async Task OnVoiceStateUpdated(DiscordClient sender, DSharpPlus.EventArgs.VoiceStateUpdateEventArgs args)
{
if (args.Guild.Id != Channel.Guild?.Id) return;
Expand Down
Loading