Skip to content
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
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# GameVault App Changelog

##1.17.1
## 1.17.2
Recommended Gamevault Server Version: `v15.0.2`
### Changes
- Added authorization to the admin panel’s registration action, allowing administrators to register users even when server-side registration is disabled.
- Extended the existing installation overwrite warning to allow users to continue the installation at their own risk.
- Performance optimization in the community tab
- Bug fix: OAuth access token expiration was not calculated correctly under certain circumstances.

## 1.17.1
Recommended Gamevault Server Version: `v15.0.2`
### Changes
- User registration has also been added to the Admin Panel.
Expand Down
2 changes: 1 addition & 1 deletion gamevault/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
[assembly: AssemblyVersion("1.17.1.0")]
[assembly: AssemblyVersion("1.17.2.0")]
[assembly: AssemblyCopyright("© Phalcode™. All Rights Reserved.")]
#if DEBUG
[assembly: XmlnsDefinition("debug-mode", "Namespace")]
Expand Down
14 changes: 11 additions & 3 deletions gamevault/Helper/LoginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public string GetServerLoginResponseMessage()
public void SwitchToOfflineMode()
{
MainWindowViewModel.Instance.OnlineState = System.Windows.Visibility.Visible;
m_User = null;
m_User = null;
}
public UserProfile GetUserProfile()
{
Expand Down Expand Up @@ -385,12 +385,20 @@ public void PhalcodeLogout()
}


public async Task<LoginState> Register(LoginUser user)
public async Task<LoginState> Register(LoginUser user, bool useOAuth = false)
{
try
{
string userObject = JsonSerializer.Serialize(new User { Username = user.Username, Password = user.Password, EMail = user.EMail, FirstName = user.FirstName, LastName = user.LastName, BirthDate = user.BirthDate });
string newUser = await WebHelper.BasePostAsync($"{user.ServerUrl}/api/auth/basic/register", userObject);
string newUser = "";
if (useOAuth)
{
newUser = await WebHelper.PostAsync($"{user.ServerUrl}/api/auth/basic/register", userObject);
}
else
{
newUser = await WebHelper.BasePostAsync($"{user.ServerUrl}/api/auth/basic/register", userObject);
}
User newUserObject = JsonSerializer.Deserialize<User>(newUser);
if (newUserObject!.Activated != true)
{
Expand Down
21 changes: 20 additions & 1 deletion gamevault/Helper/VisualHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ internal static T FindNextParentByType<T>(DependencyObject child)
while (parentDepObj != null);
return default;
}
internal static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child is T t)
{
yield return t;
}

foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
internal static void AdjustWindowChrome(MetroWindow window)
{
try
Expand All @@ -47,7 +66,7 @@ internal static void HideWindow(Window window)
internal static void RestoreHiddenWindow(Window window, int height, int width)
{
window.Width = width;
window.Height = height;
window.Height = height;
window.ShowInTaskbar = true;
double screenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
double screenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;
Expand Down
29 changes: 21 additions & 8 deletions gamevault/Helper/Web/OAuthHttpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading.Tasks;
using System.Text.Json;
using gamevault.Models;
using System.Diagnostics;

namespace gamevault.Helper
{
Expand Down Expand Up @@ -66,7 +67,7 @@ private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, Re
await LoginBasicAuthAsync(UserName, Password);
}

if (IsTokenExpired(_accessToken) && !await RefreshTokenAsync())
if (IsTokenExpired() && !await RefreshTokenAsync())
throw new InvalidOperationException("Failed to refresh token.");

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);
Expand Down Expand Up @@ -106,20 +107,30 @@ private async Task<bool> RefreshTokenAsync()

_accessToken = json?.AccessToken;
_refreshToken = json?.RefreshToken;

nextTokenRefresh = GetNextTokenRefresh(_accessToken);
//Debug.WriteLine($"Token refreshed. Next refresh at: {nextTokenRefresh}");
return true;
}

private bool IsTokenExpired(string token)
DateTimeOffset nextTokenRefresh;
private bool IsTokenExpired()
{
return nextTokenRefresh <= DateTimeOffset.UtcNow.AddMinutes(1);
}
private DateTimeOffset GetNextTokenRefresh(string token)
{
var parts = token.Split('.');
if (parts.Length != 3) return true;
if (parts.Length != 3) return DateTimeOffset.UtcNow;

var payload = parts[1];
var jsonBytes = Convert.FromBase64String(Base64UrlDecode(payload));

var json = JsonSerializer.Deserialize<JwtPayload>(Encoding.UTF8.GetString(jsonBytes));
return json?.Exp == null || DateTimeOffset.FromUnixTimeSeconds(json.Exp) <= DateTimeOffset.UtcNow.AddMinutes(1);
var json = JsonSerializer.Deserialize<JwtPayload>(Encoding.UTF8.GetString(jsonBytes));
if (json?.Exp == null)
{
return DateTimeOffset.UtcNow;
}
var expTimestamp = DateTimeOffset.FromUnixTimeSeconds(json.Exp);
var creationTimestamp = DateTimeOffset.FromUnixTimeSeconds(json.Creation);
return DateTimeOffset.UtcNow + (expTimestamp - creationTimestamp);
}

private string Base64UrlDecode(string input)
Expand All @@ -140,6 +151,8 @@ public class AuthResponse

public class JwtPayload
{
[JsonPropertyName("iat")]
public long Creation { get; set; }
[JsonPropertyName("exp")]
public long Exp { get; set; }
}
Expand Down
28 changes: 22 additions & 6 deletions gamevault/UserControls/CommunityUserControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<Viewbox Grid.RowSpan="2" Grid.ColumnSpan="3" Stretch="UniformToFill" HorizontalAlignment="Center">
<local:CacheImage ImageCacheType="UserBackground" Data="{Binding Path=CurrentShownUser}"/>
</Viewbox>
<Viewbox Stretch="UniformToFill" Grid.ColumnSpan="3" Panel.ZIndex="1">
<Viewbox Stretch="UniformToFill" Grid.ColumnSpan="3" Panel.ZIndex="1" IsVisibleChanged="LoadingPlaceholder_IsVisibleChanged">
<Viewbox.Style>
<Style TargetType="Viewbox">
<Setter Property="Visibility" Value="Collapsed"/>
Expand All @@ -80,11 +80,27 @@
</Viewbox.Style>
<Grid Background="{DynamicResource MahApps.Brushes.ThemeBackground}">
<Border Background="{DynamicResource MahApps.Brushes.ThemeBackground2}" Opacity="0.8" Width="286" CornerRadius="5" Margin="0,5,143,4"/>
<StackPanel Orientation="Horizontal" x:Name="LoadingPlaceholder" Height="450" Width="1000">
<Border x:Name="SkeletonBorder1" Style="{StaticResource SkeletonBorderStyle}" Height="77" Width="77" Margin="289,-357,0,0"/>
<StackPanel Margin="0,50,0,0">
<Border x:Name="SkeletonBorder2" Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="130" Margin="5,0,0,0" HorizontalAlignment="Left"/>
<Border x:Name="SkeletonBorder3" Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="195" Margin="5,5,0,0"/>
<StackPanel x:Name="LoadingPlaceholder" Width="1000">
<StackPanel Orientation="Horizontal" Height="450" Width="1000">
<Border Style="{StaticResource SkeletonBorderStyle}" Height="77" Width="77" Margin="289,-357,0,0"/>
<StackPanel Margin="0,50,0,0">
<Border Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="130" Margin="5,0,0,0" HorizontalAlignment="Left"/>
<Border Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="195" Margin="5,5,0,0"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" Height="100" Width="300" Margin="-119,-571,0,0">
<Border Style="{StaticResource SkeletonBorderStyle}" Height="54" Width="54" Margin="0,-84,0,0" HorizontalAlignment="Left"/>
<StackPanel>
<Border Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="130" Margin="5,0,0,0" HorizontalAlignment="Left"/>
<Border Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="195" Margin="5,5,0,0"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" Height="100" Width="300" Margin="-119,-441,0,0">
<Border Style="{StaticResource SkeletonBorderStyle}" Height="54" Width="54" Margin="0,-84,0,0" HorizontalAlignment="Left"/>
<StackPanel>
<Border Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="130" Margin="5,0,0,0" HorizontalAlignment="Left"/>
<Border Style="{StaticResource SkeletonBorderStyle}" Height="15" Width="195" Margin="5,5,0,0"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>
Expand Down
41 changes: 37 additions & 4 deletions gamevault/UserControls/CommunityUserControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public partial class CommunityUserControl : UserControl
private int forceShowId = -1;

private Storyboard skeletonAnimation;
private List<Storyboard> runningSkeletonAnimations = new List<Storyboard>();
private List<Border> skeletonBorders = new List<Border>();
public CommunityUserControl()
{
Expand Down Expand Up @@ -55,23 +56,55 @@ private void InitUserLoadingAnimation()
{
try
{
if (skeletonAnimation != null)
return; //Already initialized

ViewModel.LoadingUser = true;
skeletonAnimation = (Storyboard)FindResource("SkeletonLoadingAnimation");
skeletonBorders.Add((Border)FindName("SkeletonBorder1"));
skeletonBorders.Add((Border)FindName("SkeletonBorder2"));
skeletonBorders.Add((Border)FindName("SkeletonBorder3"));
Style? skeletonBorderStyle = FindResource("SkeletonBorderStyle") as Style;
foreach (var border in VisualHelper.FindVisualChildren<Border>(LoadingPlaceholder))
{
if (border.Style == skeletonBorderStyle)
{
skeletonBorders.Add(border);
}
}
if (skeletonAnimation != null)
{
foreach (var border in skeletonBorders)
{
skeletonAnimation = skeletonAnimation.Clone();
Storyboard.SetTarget(skeletonAnimation, border);
skeletonAnimation.Begin();
skeletonAnimation.Begin(border, true);
runningSkeletonAnimations.Add(skeletonAnimation);
}
LoadingPlaceholder.Visibility = Visibility.Visible;
}
}
catch { }
}
private void LoadingPlaceholder_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (skeletonAnimation == null)
return;


Visibility visibility = ((FrameworkElement)sender).Visibility;
if (visibility == Visibility.Collapsed)
{
for (int i = 0; i < runningSkeletonAnimations.Count; i++)
{
runningSkeletonAnimations[i].Pause(skeletonBorders[i]);
}
}
else if (visibility == Visibility.Visible)
{
for (int i = 0; i < runningSkeletonAnimations.Count; i++)
{
runningSkeletonAnimations[i].Resume(skeletonBorders[i]);
}
}
}
public async Task InitUserList()
{
string result = await WebHelper.GetAsync(@$"{SettingsViewModel.Instance.ServerUrl}/api/users");
Expand Down
8 changes: 6 additions & 2 deletions gamevault/UserControls/GameDownloadUserControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,12 @@ private async Task Install()
{
if (InstallViewModel.Instance.InstalledGames.Any(game => game.Key.ID == ViewModel.Game.ID))
{
await ((MetroWindow)App.Current.MainWindow).ShowMessageAsync($"The Game {ViewModel.Game.Title} is already installed at \n'{InstallViewModel.Instance.InstalledGames.First(game => game.Key.ID == ViewModel.Game.ID).Value}'", "", MessageDialogStyle.Affirmative, new MetroDialogSettings() { AffirmativeButtonText = "Ok", AnimateHide = false });
return;
MessageDialogResult result = await ((MetroWindow)App.Current.MainWindow).ShowMessageAsync($"The Game {ViewModel.Game.Title} is already installed at \n'{InstallViewModel.Instance.InstalledGames.First(game => game.Key.ID == ViewModel.Game.ID).Value}'" +
$"\nWarning: Overwriting an existing installation with a new one may cause data corruption.", "",
MessageDialogStyle.AffirmativeAndNegative, new MetroDialogSettings() { AffirmativeButtonText = "Continue", NegativeButtonText = "Cancel", AnimateHide = false });

if (result == MessageDialogResult.Negative)
return;
}
string targedDir = (SettingsViewModel.Instance.MountIso && Directory.Exists(mountedDrive)) ? mountedDrive : $"{m_DownloadPath}\\Extract";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ private async void RegisterNewUser_Click(object sender, RoutedEventArgs e)
try
{
ValidateSignUpData();
LoginState state = await LoginManager.Instance.Register(ViewModel.SignupUser);
LoginState state = await LoginManager.Instance.Register(ViewModel.SignupUser, true);
if (state != LoginState.Error)
{
MainWindowViewModel.Instance.ClosePopup();
if(MainWindowViewModel.Instance.ActiveControl.GetType() == typeof(AdminConsoleUserControl))
if (MainWindowViewModel.Instance.ActiveControl.GetType() == typeof(AdminConsoleUserControl))
{
await MainWindowViewModel.Instance.AdminConsole.InitUserList();
}
Expand Down
Loading