Merge branch 'main' into user-pfp-discord-rpc

This commit is contained in:
axell 2024-09-05 15:24:18 +02:00 committed by GitHub
commit 3855ac68dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 149 additions and 115 deletions

9
Bloxstrap/GlobalCache.cs Normal file
View File

@ -0,0 +1,9 @@
namespace Bloxstrap
{
public static class GlobalCache
{
public static readonly Dictionary<string, Task> PendingTasks = new();
public static readonly Dictionary<string, string> ServerLocation = new();
}
}

View File

@ -494,7 +494,7 @@ namespace Bloxstrap
string oldStartPath = Path.Combine(Paths.WindowsStartMenu, "Bloxstrap"); string oldStartPath = Path.Combine(Paths.WindowsStartMenu, "Bloxstrap");
if (File.Exists(oldDesktopPath)) if (File.Exists(oldDesktopPath))
File.Move(oldDesktopPath, DesktopShortcut); File.Move(oldDesktopPath, DesktopShortcut, true);
if (Directory.Exists(oldStartPath)) if (Directory.Exists(oldStartPath))
{ {

View File

@ -127,7 +127,6 @@ namespace Bloxstrap.Integrations
} }
} }
// TODO: i need to double check how this handles failed game joins (connection error, invalid permissions, etc)
private void ReadLogEntry(string entry) private void ReadLogEntry(string entry)
{ {
const string LOG_IDENT = "ActivityWatcher::ReadLogEntry"; const string LOG_IDENT = "ActivityWatcher::ReadLogEntry";
@ -144,7 +143,17 @@ namespace Bloxstrap.Integrations
App.Logger.WriteLine(LOG_IDENT, $"Read {_logEntriesRead} log entries"); App.Logger.WriteLine(LOG_IDENT, $"Read {_logEntriesRead} log entries");
if (entry.Contains(GameLeavingEntry)) if (entry.Contains(GameLeavingEntry))
OnAppClose?.Invoke(this, new EventArgs()); {
App.Logger.WriteLine(LOG_IDENT, "User is back into the desktop app");
OnAppClose?.Invoke(this, EventArgs.Empty);
if (Data.PlaceId != 0 && !InGame)
{
App.Logger.WriteLine(LOG_IDENT, "User appears to be leaving from a cancelled/errored join");
Data = new();
}
}
if (!InGame && Data.PlaceId == 0) if (!InGame && Data.PlaceId == 0)
{ {
@ -183,6 +192,9 @@ namespace Bloxstrap.Integrations
Data.JobId = match.Groups[1].Value; Data.JobId = match.Groups[1].Value;
Data.MachineAddress = match.Groups[3].Value; Data.MachineAddress = match.Groups[3].Value;
if (App.Settings.Prop.ShowServerDetails && Data.MachineAddressValid)
_ = Data.QueryServerLocation();
if (_teleportMarker) if (_teleportMarker)
{ {
Data.IsTeleport = true; Data.IsTeleport = true;
@ -195,7 +207,7 @@ namespace Bloxstrap.Integrations
_reservedTeleportMarker = false; _reservedTeleportMarker = false;
} }
App.Logger.WriteLine(LOG_IDENT, $"Joining Game ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); App.Logger.WriteLine(LOG_IDENT, $"Joining Game ({Data})");
} }
} }
else if (!InGame && Data.PlaceId != 0) else if (!InGame && Data.PlaceId != 0)
@ -234,7 +246,7 @@ namespace Bloxstrap.Integrations
{ {
var lastActivity = History.First(); var lastActivity = History.First();
if (lastActivity is not null && Data.UniverseId == lastActivity.UniverseId && Data.IsTeleport) if (Data.UniverseId == lastActivity.UniverseId && Data.IsTeleport)
Data.RootActivity = lastActivity.RootActivity ?? lastActivity; Data.RootActivity = lastActivity.RootActivity ?? lastActivity;
} }
} }
@ -251,7 +263,10 @@ namespace Bloxstrap.Integrations
Data.MachineAddress = match.Groups[1].Value; Data.MachineAddress = match.Groups[1].Value;
App.Logger.WriteLine(LOG_IDENT, $"Server is UDMUX protected ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); if (App.Settings.Prop.ShowServerDetails)
_ = Data.QueryServerLocation();
App.Logger.WriteLine(LOG_IDENT, $"Server is UDMUX protected ({Data})");
} }
else if (entry.Contains(GameJoinedEntry)) else if (entry.Contains(GameJoinedEntry))
{ {
@ -264,7 +279,7 @@ namespace Bloxstrap.Integrations
return; return;
} }
App.Logger.WriteLine(LOG_IDENT, $"Joined Game ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); App.Logger.WriteLine(LOG_IDENT, $"Joined Game ({Data})");
InGame = true; InGame = true;
Data.TimeJoined = DateTime.Now; Data.TimeJoined = DateTime.Now;
@ -278,7 +293,7 @@ namespace Bloxstrap.Integrations
if (entry.Contains(GameDisconnectedEntry)) if (entry.Contains(GameDisconnectedEntry))
{ {
App.Logger.WriteLine(LOG_IDENT, $"Disconnected from Game ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); App.Logger.WriteLine(LOG_IDENT, $"Disconnected from Game ({Data})");
Data.TimeLeft = DateTime.Now; Data.TimeLeft = DateTime.Now;
History.Insert(0, Data); History.Insert(0, Data);
@ -290,7 +305,7 @@ namespace Bloxstrap.Integrations
} }
else if (entry.Contains(GameTeleportingEntry)) else if (entry.Contains(GameTeleportingEntry))
{ {
App.Logger.WriteLine(LOG_IDENT, $"Initiating teleport to server ({Data.PlaceId}/{Data.JobId}/{Data.MachineAddress})"); App.Logger.WriteLine(LOG_IDENT, $"Initiating teleport to server ({Data})");
_teleportMarker = true; _teleportMarker = true;
} }
else if (_teleportMarker && entry.Contains(GameJoiningReservedServerEntry)) else if (_teleportMarker && entry.Contains(GameJoiningReservedServerEntry))
@ -378,44 +393,6 @@ namespace Bloxstrap.Integrations
} }
} }
public async Task<string> GetServerLocation()
{
const string LOG_IDENT = "ActivityWatcher::GetServerLocation";
if (GeolocationCache.ContainsKey(Data.MachineAddress))
return GeolocationCache[Data.MachineAddress];
try
{
string location = "";
var ipInfo = await Http.GetJson<IPInfoResponse>($"https://ipinfo.io/{Data.MachineAddress}/json");
if (String.IsNullOrEmpty(ipInfo.City))
throw new InvalidHTTPResponseException("Reported city was blank");
if (ipInfo.City == ipInfo.Region)
location = $"{ipInfo.Region}, {ipInfo.Country}";
else
location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}";
GeolocationCache[Data.MachineAddress] = location;
if (!InGame)
return "";
return location;
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {Data.MachineAddress}");
App.Logger.WriteException(LOG_IDENT, ex);
Frontend.ShowMessageBox($"{Strings.ActivityWatcher_LocationQueryFailed}\n\n{ex.Message}", MessageBoxImage.Warning);
return "?";
}
}
public void Dispose() public void Dispose()
{ {
IsDisposed = true; IsDisposed = true;

View File

@ -1,4 +1,5 @@
using System.Web; using System.Web;
using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
@ -38,6 +39,8 @@ namespace Bloxstrap.Models
public string UserId { get; set; } = String.Empty; public string UserId { get; set; } = String.Empty;
public bool MachineAddressValid => !String.IsNullOrEmpty(MachineAddress) && !MachineAddress.StartsWith("10.");
public bool IsTeleport { get; set; } = false; public bool IsTeleport { get; set; } = false;
public ServerType ServerType { get; set; } = ServerType.Public; public ServerType ServerType { get; set; } = ServerType.Public;
@ -85,6 +88,55 @@ namespace Bloxstrap.Models
return deeplink; return deeplink;
} }
public async Task<string> QueryServerLocation()
{
const string LOG_IDENT = "ActivityData::QueryServerLocation";
if (!MachineAddressValid)
throw new InvalidOperationException($"Machine address is invalid ({MachineAddress})");
if (GlobalCache.PendingTasks.TryGetValue(MachineAddress, out Task? task))
await task;
if (GlobalCache.ServerLocation.TryGetValue(MachineAddress, out string? location))
return location;
try
{
location = "";
var ipInfoTask = Http.GetJson<IPInfoResponse>($"https://ipinfo.io/{MachineAddress}/json");
GlobalCache.PendingTasks.Add(MachineAddress, ipInfoTask);
var ipInfo = await ipInfoTask;
GlobalCache.PendingTasks.Remove(MachineAddress);
if (String.IsNullOrEmpty(ipInfo.City))
throw new InvalidHTTPResponseException("Reported city was blank");
if (ipInfo.City == ipInfo.Region)
location = $"{ipInfo.Region}, {ipInfo.Country}";
else
location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}";
GlobalCache.ServerLocation[MachineAddress] = location;
return location;
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {MachineAddress}");
App.Logger.WriteException(LOG_IDENT, ex);
Frontend.ShowMessageBox($"{Strings.ActivityWatcher_LocationQueryFailed}\n\n{ex.Message}", MessageBoxImage.Warning);
return "?";
}
}
public override string ToString() => $"{PlaceId}/{JobId}";
private void RejoinServer() private void RejoinServer()
{ {
string playerPath = Path.Combine(Paths.Versions, App.State.Prop.PlayerVersionGuid, "RobloxPlayerBeta.exe"); string playerPath = Path.Combine(Paths.Versions, App.State.Prop.PlayerVersionGuid, "RobloxPlayerBeta.exe");

View File

@ -719,15 +719,6 @@ namespace Bloxstrap.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Loading, please wait....
/// </summary>
public static string ContextMenu_ServerInformation_Loading {
get {
return ResourceManager.GetString("ContextMenu.ServerInformation.Loading", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Location. /// Looks up a localized string similar to Location.
/// </summary> /// </summary>
@ -2727,7 +2718,25 @@ namespace Bloxstrap.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running.. /// Looks up a localized string similar to When in-game, you&apos;ll be able to see where your server is located via [ipinfo.io]({0})..
/// </summary>
public static string Menu_Integrations_QueryServerLocation_Description {
get {
return ResourceManager.GetString("Menu.Integrations.QueryServerLocation.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Query server location.
/// </summary>
public static string Menu_Integrations_QueryServerLocation_Title {
get {
return ResourceManager.GetString("Menu.Integrations.QueryServerLocation.Title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. [Find out more]({0})..
/// </summary> /// </summary>
public static string Menu_Integrations_RequiresActivityTracking { public static string Menu_Integrations_RequiresActivityTracking {
get { get {
@ -2753,24 +2762,6 @@ namespace Bloxstrap.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to When you join a game, you&apos;ll be notified of where your server&apos;s located. Won&apos;t show in fullscreen..
/// </summary>
public static string Menu_Integrations_ShowServerDetails_Description {
get {
return ResourceManager.GetString("Menu.Integrations.ShowServerDetails.Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to See server location when joining a game.
/// </summary>
public static string Menu_Integrations_ShowServerDetails_Title {
get {
return ResourceManager.GetString("Menu.Integrations.ShowServerDetails.Title", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Integrations. /// Looks up a localized string similar to Integrations.
/// </summary> /// </summary>

View File

@ -268,9 +268,6 @@ Your ReShade configuration files will still be saved, and you can locate them by
<data name="ContextMenu.ServerInformation.InstanceId" xml:space="preserve"> <data name="ContextMenu.ServerInformation.InstanceId" xml:space="preserve">
<value>Instance ID</value> <value>Instance ID</value>
</data> </data>
<data name="ContextMenu.ServerInformation.Loading" xml:space="preserve">
<value>Loading, please wait...</value>
</data>
<data name="ContextMenu.ServerInformation.Location" xml:space="preserve"> <data name="ContextMenu.ServerInformation.Location" xml:space="preserve">
<value>Location</value> <value>Location</value>
</data> </data>
@ -761,7 +758,7 @@ Selecting 'No' will ignore this warning and continue installation.</value>
<value>Enable activity tracking</value> <value>Enable activity tracking</value>
</data> </data>
<data name="Menu.Integrations.RequiresActivityTracking" xml:space="preserve"> <data name="Menu.Integrations.RequiresActivityTracking" xml:space="preserve">
<value>This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running.</value> <value>This feature requires activity tracking to be enabled and the Discord desktop app to be installed and running. [Find out more]({0}).</value>
</data> </data>
<data name="Menu.Integrations.ShowGameActivity.Description" xml:space="preserve"> <data name="Menu.Integrations.ShowGameActivity.Description" xml:space="preserve">
<value>The Roblox game you're playing will be shown on your Discord profile. [Not working?]({0})</value> <value>The Roblox game you're playing will be shown on your Discord profile. [Not working?]({0})</value>
@ -769,11 +766,11 @@ Selecting 'No' will ignore this warning and continue installation.</value>
<data name="Menu.Integrations.ShowGameActivity.Title" xml:space="preserve"> <data name="Menu.Integrations.ShowGameActivity.Title" xml:space="preserve">
<value>Show game activity</value> <value>Show game activity</value>
</data> </data>
<data name="Menu.Integrations.ShowServerDetails.Description" xml:space="preserve"> <data name="Menu.Integrations.QueryServerLocation.Description" xml:space="preserve">
<value>When you join a game, you'll be notified of where your server's located. Won't show in fullscreen.</value> <value>When in-game, you'll be able to see where your server is located via [ipinfo.io]({0}).</value>
</data> </data>
<data name="Menu.Integrations.ShowServerDetails.Title" xml:space="preserve"> <data name="Menu.Integrations.QueryServerLocation.Title" xml:space="preserve">
<value>See server location when joining a game</value> <value>Query server location</value>
</data> </data>
<data name="Menu.Integrations.Title" xml:space="preserve"> <data name="Menu.Integrations.Title" xml:space="preserve">
<value>Integrations</value> <value>Integrations</value>

View File

@ -28,6 +28,8 @@ namespace Bloxstrap.UI.Elements.ContextMenu
private ServerInformation? _serverInformationWindow; private ServerInformation? _serverInformationWindow;
private ServerHistory? _gameHistoryWindow;
public MenuContainer(Watcher watcher) public MenuContainer(Watcher watcher)
{ {
InitializeComponent(); InitializeComponent();
@ -51,14 +53,14 @@ namespace Bloxstrap.UI.Elements.ContextMenu
{ {
if (_serverInformationWindow is null) if (_serverInformationWindow is null)
{ {
_serverInformationWindow = new ServerInformation(_watcher); _serverInformationWindow = new(_watcher);
_serverInformationWindow.Closed += (_, _) => _serverInformationWindow = null; _serverInformationWindow.Closed += (_, _) => _serverInformationWindow = null;
} }
if (!_serverInformationWindow.IsVisible) if (!_serverInformationWindow.IsVisible)
_serverInformationWindow.Show(); _serverInformationWindow.ShowDialog();
else
_serverInformationWindow.Activate(); _serverInformationWindow.Activate();
} }
public void ActivityWatcher_OnLogOpen(object? sender, EventArgs e) => public void ActivityWatcher_OnLogOpen(object? sender, EventArgs e) =>
@ -135,7 +137,16 @@ namespace Bloxstrap.UI.Elements.ContextMenu
if (_activityWatcher is null) if (_activityWatcher is null)
throw new ArgumentNullException(nameof(_activityWatcher)); throw new ArgumentNullException(nameof(_activityWatcher));
new ServerHistory(_activityWatcher).ShowDialog(); if (_gameHistoryWindow is null)
{
_gameHistoryWindow = new(_activityWatcher);
_gameHistoryWindow.Closed += (_, _) => _gameHistoryWindow = null;
}
if (!_gameHistoryWindow.IsVisible)
_gameHistoryWindow.ShowDialog();
else
_gameHistoryWindow.Activate();
} }
} }
} }

View File

@ -46,14 +46,14 @@
<TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_InstanceId}" /> <TextBlock Grid.Row="1" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_InstanceId}" />
<TextBlock Grid.Row="1" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding InstanceId, Mode=OneWay}" /> <TextBlock Grid.Row="1" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding InstanceId, Mode=OneWay}" />
<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_Location}" /> <TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,16,12" VerticalAlignment="Center" Text="{x:Static resources:Strings.ContextMenu_ServerInformation_Location}" Visibility="{Binding ServerLocationVisibility, Mode=OneTime}" />
<TextBlock Grid.Row="2" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding ServerLocation, Mode=OneWay}" /> <TextBlock Grid.Row="2" Grid.Column="1" Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{Binding ServerLocation, Mode=OneWay}" Visibility="{Binding ServerLocationVisibility, Mode=OneTime}" />
</Grid> </Grid>
<Border Grid.Row="2" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}"> <Border Grid.Row="2" Padding="15" Background="{ui:ThemeResource SolidBackgroundFillColorSecondaryBrush}">
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right"> <StackPanel Orientation="Horizontal" FlowDirection="LeftToRight" HorizontalAlignment="Right">
<Button MinWidth="100" Content="{x:Static resources:Strings.ContextMenu_ServerInformation_CopyInstanceId}" Command="{Binding CopyInstanceIdCommand, Mode=OneTime}" /> <Button MinWidth="100" Content="{x:Static resources:Strings.ContextMenu_ServerInformation_CopyInstanceId}" Command="{Binding CopyInstanceIdCommand, Mode=OneTime}" />
<Button Margin="12,0,0,0" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" Command="{Binding CloseWindowCommand, Mode=OneTime}" /> <Button Margin="12,0,0,0" MinWidth="100" Content="{x:Static resources:Strings.Common_Close}" IsCancel="True" />
</StackPanel> </StackPanel>
</Border> </Border>
</Grid> </Grid>

View File

@ -24,11 +24,7 @@ namespace Bloxstrap.UI.Elements.ContextMenu
{ {
public ServerInformation(Watcher watcher) public ServerInformation(Watcher watcher)
{ {
var viewModel = new ServerInformationViewModel(watcher); DataContext = new ServerInformationViewModel(watcher);
viewModel.RequestCloseEvent += (_, _) => Close();
DataContext = viewModel;
InitializeComponent(); InitializeComponent();
} }
} }

View File

@ -26,8 +26,9 @@
</controls:OptionControl> </controls:OptionControl>
<controls:OptionControl <controls:OptionControl
Header="{x:Static resources:Strings.Menu_Integrations_ShowServerDetails_Title}" Header="{x:Static resources:Strings.Menu_Integrations_QueryServerLocation_Title}"
Description="{x:Static resources:Strings.Menu_Integrations_ShowServerDetails_Description}" Description="{Binding Source={x:Static resources:Strings.Menu_Integrations_QueryServerLocation_Description}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://ipinfo.io'}"
HelpLink="https://github.com/pizzaboxer/bloxstrap/wiki/What-is-activity-tracking%3F#server-location-querying"
IsEnabled="{Binding InnerContent.IsChecked, ElementName=ActivityTrackingOption, Mode=OneWay}"> IsEnabled="{Binding InnerContent.IsChecked, ElementName=ActivityTrackingOption, Mode=OneWay}">
<ui:ToggleSwitch IsChecked="{Binding ShowServerDetailsEnabled, Mode=TwoWay}" /> <ui:ToggleSwitch IsChecked="{Binding ShowServerDetailsEnabled, Mode=TwoWay}" />
</controls:OptionControl> </controls:OptionControl>
@ -40,7 +41,7 @@
</controls:OptionControl> </controls:OptionControl>
<TextBlock Text="{x:Static resources:Strings.Common_DiscordRichPresence}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" /> <TextBlock Text="{x:Static resources:Strings.Common_DiscordRichPresence}" FontSize="20" FontWeight="Medium" Margin="0,16,0,0" />
<TextBlock Text="{x:Static resources:Strings.Menu_Integrations_RequiresActivityTracking}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" /> <controls:MarkdownTextBlock MarkdownText="{Binding Source={x:Static resources:Strings.Menu_Integrations_RequiresActivityTracking}, Converter={StaticResource StringFormatConverter}, ConverterParameter='https://github.com/pizzaboxer/bloxstrap/wiki/What-is-activity-tracking%3F#discord-rich-presence'}" TextWrapping="Wrap" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
<controls:OptionControl <controls:OptionControl
Header="{x:Static resources:Strings.Menu_Integrations_ShowGameActivity_Title}" Header="{x:Static resources:Strings.Menu_Integrations_ShowGameActivity_Title}"

View File

@ -26,7 +26,7 @@ namespace Bloxstrap.UI
_watcher = watcher; _watcher = watcher;
_notifyIcon = new() _notifyIcon = new(new System.ComponentModel.Container())
{ {
Icon = Properties.Resources.IconBloxstrap, Icon = Properties.Resources.IconBloxstrap,
Text = App.ProjectName, Text = App.ProjectName,
@ -35,7 +35,7 @@ namespace Bloxstrap.UI
_notifyIcon.MouseClick += MouseClickEventHandler; _notifyIcon.MouseClick += MouseClickEventHandler;
if (_activityWatcher is not null) if (_activityWatcher is not null && App.Settings.Prop.ShowServerDetails)
_activityWatcher.OnGameJoin += OnGameJoin; _activityWatcher.OnGameJoin += OnGameJoin;
_menuContainer = new(_watcher); _menuContainer = new(_watcher);
@ -59,7 +59,7 @@ namespace Bloxstrap.UI
if (_activityWatcher is null) if (_activityWatcher is null)
return; return;
string serverLocation = await _activityWatcher.GetServerLocation(); string serverLocation = await _activityWatcher.Data.QueryServerLocation();
if (string.IsNullOrEmpty(serverLocation)) if (string.IsNullOrEmpty(serverLocation))
return; return;
@ -81,6 +81,7 @@ namespace Bloxstrap.UI
} }
#endregion #endregion
// we may need to create our own handler for this, because this sorta sucks
public void ShowAlert(string caption, string message, int duration, EventHandler? clickHandler) public void ShowAlert(string caption, string message, int duration, EventHandler? clickHandler)
{ {
string id = Guid.NewGuid().ToString()[..8]; string id = Guid.NewGuid().ToString()[..8];

View File

@ -36,8 +36,7 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu
if (entries.Any()) if (entries.Any())
{ {
// TODO: this will duplicate universe ids string universeIds = String.Join(',', entries.GroupBy(x => x.UniverseId).Select(x => x.First()));
string universeIds = String.Join(',', entries.Select(x => x.UniverseId));
try try
{ {

View File

@ -13,27 +13,28 @@ namespace Bloxstrap.UI.ViewModels.ContextMenu
public string ServerType => _activityWatcher.Data.ServerType.ToTranslatedString(); public string ServerType => _activityWatcher.Data.ServerType.ToTranslatedString();
public string ServerLocation { get; private set; } = Strings.ContextMenu_ServerInformation_Loading; public string ServerLocation { get; private set; } = Strings.Common_Loading;
public Visibility ServerLocationVisibility => App.Settings.Prop.ShowServerDetails ? Visibility.Visible : Visibility.Collapsed;
public ICommand CopyInstanceIdCommand => new RelayCommand(CopyInstanceId); public ICommand CopyInstanceIdCommand => new RelayCommand(CopyInstanceId);
public ICommand CloseWindowCommand => new RelayCommand(RequestClose);
public EventHandler? RequestCloseEvent; public EventHandler? RequestCloseEvent;
public ServerInformationViewModel(Watcher watcher) public ServerInformationViewModel(Watcher watcher)
{ {
_activityWatcher = watcher.ActivityWatcher!; _activityWatcher = watcher.ActivityWatcher!;
Task.Run(async () => if (ServerLocationVisibility == Visibility.Visible)
{ QueryServerLocation();
ServerLocation = await _activityWatcher.GetServerLocation(); }
OnPropertyChanged(nameof(ServerLocation));
}); public async void QueryServerLocation()
{
ServerLocation = await _activityWatcher.Data.QueryServerLocation();
OnPropertyChanged(nameof(ServerLocation));
} }
private void CopyInstanceId() => Clipboard.SetDataObject(InstanceId); private void CopyInstanceId() => Clipboard.SetDataObject(InstanceId);
private void RequestClose() => RequestCloseEvent?.Invoke(this, EventArgs.Empty);
} }
} }

View File

@ -1,6 +1,5 @@
namespace Bloxstrap.UI.ViewModels.Installer namespace Bloxstrap.UI.ViewModels.Installer
{ {
// TODO: administrator check?
public class WelcomeViewModel : NotifyPropertyChangedViewModel public class WelcomeViewModel : NotifyPropertyChangedViewModel
{ {
// formatting is done here instead of in xaml, it's just a bit easier // formatting is done here instead of in xaml, it's just a bit easier