Skip to content

Commit

Permalink
Fix nullability issues (#213)
Browse files Browse the repository at this point in the history
* Fix nullabiliy in .xaml.cs

* Fix nullability in navigation

* Fix ChooseModLoaderData

* Fix NewsData
  • Loading branch information
gaviny82 authored Feb 28, 2024
1 parent ac19499 commit d4ae8be
Show file tree
Hide file tree
Showing 31 changed files with 224 additions and 96 deletions.
7 changes: 4 additions & 3 deletions Natsurainko.FluentLauncher/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ namespace Natsurainko.FluentLauncher;
public partial class App : Application
{
public static IServiceProvider Services { get; } = ConfigureServices();
public static T GetService<T>() => Services.GetService<T>();
public static MainWindow MainWindow { get; set; }
public static DispatcherQueue DispatcherQueue { get; private set; }
public static T GetService<T>() => Services.GetService<T>()
?? throw new InvalidOperationException($"Service of type {typeof(T).Name} not found.");
public static MainWindow MainWindow { get; set; } = null!;
public static DispatcherQueue DispatcherQueue { get; private set; } = null!;

public App()
{
Expand Down
60 changes: 43 additions & 17 deletions Natsurainko.FluentLauncher/Classes/Data/UI/ChooseModLoaderData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ public ChooseModLoaderData(ModLoaderType type, VersionManifestItem manifestItem,
private string displayText = ResourceUtils.GetValue("CoreInstallWizard", "ChooseModLoaderPage", "_Loading");

[ObservableProperty]
private IEnumerable<LoaderBuildData> items;
private IEnumerable<LoaderBuildData>? items;

[ObservableProperty]
private LoaderBuildData selectedItem;
private LoaderBuildData? selectedItem;

private void Init() => Task.Run(() =>
{
Expand All @@ -74,20 +74,31 @@ private void Init() => Task.Run(() =>
using var responseMessage = HttpUtils.HttpGet(url);
responseMessage.EnsureSuccessStatusCode();

var array = JsonNode.Parse(responseMessage.Content.ReadAsString()).AsArray();
IEnumerable<LoaderBuildData> loaders = default;
var array = responseMessage.Content.ReadAsString()
.ToJsonNode()?
.AsArray()
.WhereNotNull();
IEnumerable<LoaderBuildData>? loaders = null;

if (array.Any())
if (array is not null && array.Any())
{
switch (Type)
{
case ModLoaderType.Forge:

var forge = array.Select(x => new LoaderBuildData
var forge = array.Select(x =>
{
DisplayText = x["version"].GetValue<string>(),
Metadata = x["build"]
}).ToList();
var displayText = x["version"]?.GetValue<string>();
var metadata = x["build"];
if (displayText is null || metadata is null)
throw new Exception("Invalid forge data");

return new LoaderBuildData
{
DisplayText = displayText,
Metadata = metadata
};
}).WhereNotNull().ToList();

forge.Sort((a, b) => a.Metadata.GetValue<int>().CompareTo(b.Metadata.GetValue<int>()));
forge.Reverse();
Expand All @@ -97,21 +108,36 @@ private void Init() => Task.Run(() =>
break;
case ModLoaderType.Fabric:

var fabric = array.Select(x => new LoaderBuildData
var fabric = array.Select(x =>
{
DisplayText = x["loader"]["version"].GetValue<string>(),
Metadata = x
var displayText = x["loader"]?["version"]?.GetValue<string>();
if (displayText is null)
throw new Exception("Invalid fabric data");

return new LoaderBuildData
{
DisplayText = displayText,
Metadata = x
};
}).ToList();

loaders = fabric;

break;
case ModLoaderType.OptiFine:

var optifine = array.Select(x => new LoaderBuildData
var optifine = array.Select(x =>
{
DisplayText = $"{x["type"].GetValue<string>()}_{x["patch"].GetValue<string>()}",
Metadata = x
var type = x["type"]?.GetValue<string>();
var patch = x["patch"]?.GetValue<string>();
if (type is null || patch is null)
throw new Exception("Invalid optifine data");

return new LoaderBuildData
{
DisplayText = $"{type}_{patch}",
Metadata = x
};
}).ToList();

loaders = optifine;
Expand Down Expand Up @@ -213,8 +239,8 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e)

public class LoaderBuildData
{
public string DisplayText { get; set; }
public required string DisplayText { get; set; }

public JsonNode Metadata { get; set; }
public required JsonNode Metadata { get; set; }
}
}
30 changes: 23 additions & 7 deletions Natsurainko.FluentLauncher/Classes/Data/UI/NewsData.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace Natsurainko.FluentLauncher.Classes.Data.UI;

internal class NewsData
{
public string ImageUrl { get; set; }
public string? ImageUrl { get; set; } = null!;

[JsonPropertyName("title")]
public string Title { get; set; }
public string? Title { get; set; }

[JsonPropertyName("tag")]
public string Tag { get; set; }
public string? Tag { get; set; }

[JsonPropertyName("date")]
public string Date { get; set; }
public string? Date { get; set; }

[JsonPropertyName("text")]
public string Text { get; set; }
public string? Text { get; set; }

[JsonPropertyName("readMoreLink")]
public string ReadMoreUrl { get; set; }
public string? ReadMoreUrl { get; set; }


public static NewsData Deserialize(JsonNode node)
{
NewsData? data = node.Deserialize<NewsData>();
if (data is null)
throw new JsonException("Failed to deserialize news data");

string urlPath = node["newsPageImage"]?["url"]?.GetValue<string>()
?? throw new JsonException("Failed to get news image URL");
data.ImageUrl = $"https://launchercontent.mojang.com{urlPath}";

return data;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Natsurainko.FluentLauncher.Classes.Data.UI;
using Natsurainko.FluentLauncher.Services.Settings;
using Natsurainko.FluentLauncher.Utils;
using Nrk.FluentCore.Resources;
using Nrk.FluentCore.Utils;
using System;
Expand Down Expand Up @@ -108,15 +109,16 @@ public Task<NewsData[]> FetchNews()
_fetchNewsTask = Task.Run(() =>
{
var localFile = GetLocalFileOfInterface("https://launchercontent.mojang.com/news.json", autoRefresh: true);
if (string.IsNullOrEmpty(localFile)) throw new ArgumentNullException(nameof(localFile));
if (string.IsNullOrEmpty(localFile))
throw new ArgumentNullException(nameof(localFile));

return JsonNode.Parse(File.ReadAllText(localFile))["entries"].AsArray()
.Select(x =>
{
var contentData = x.Deserialize<NewsData>();
contentData.ImageUrl = $"https://launchercontent.mojang.com{x["newsPageImage"]["url"].GetValue<string>()}";
return contentData;
}).ToArray();
var entries = JsonNodeUtils.ParseFile(localFile)?["entries"]
?? throw new JsonException("Failed to get news entries");

return entries
.AsArray()
.WhereNotNull()
.Select(x => NewsData.Deserialize(x)).ToArray();
});

return _fetchNewsTask;
Expand Down
24 changes: 11 additions & 13 deletions Natsurainko.FluentLauncher/Services/UI/AppearanceService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ namespace Natsurainko.FluentLauncher.Services.UI;
internal class AppearanceService
{
private readonly SettingsService _settingsService;
private NavigationView _navigationView;
private BitmapImage backgroundImage;
private NavigationView? _navigationView;
private BitmapImage? backgroundImage;

public Type HomePageType => _settingsService.UseNewHomePage ? typeof(NewHomePage) : typeof(HomePage);

Expand All @@ -29,21 +29,19 @@ public AppearanceService(SettingsService settingsService)
_settingsService = settingsService;
}

private void NavigationViewDisplayModeChanged(AppSettingsManagement.SettingsContainer sender, AppSettingsManagement.SettingChangedEventArgs e)
{
_navigationView.PaneDisplayMode = _settingsService.NavigationViewDisplayMode == 0
? NavigationViewPaneDisplayMode.Auto
: NavigationViewPaneDisplayMode.LeftMinimal;
}

public void RegisterNavigationView(NavigationView navigationView)
{
_navigationView = navigationView;
_navigationView.PaneDisplayMode = _settingsService.NavigationViewDisplayMode == 0
? NavigationViewPaneDisplayMode.Auto
: NavigationViewPaneDisplayMode.LeftMinimal;

_settingsService.NavigationViewDisplayModeChanged += NavigationViewDisplayModeChanged;
_settingsService.NavigationViewDisplayModeChanged += (sender, e)=>
{
_navigationView.PaneDisplayMode = _settingsService.NavigationViewDisplayMode == 0
? NavigationViewPaneDisplayMode.Auto
: NavigationViewPaneDisplayMode.LeftMinimal;
};
}

public void ApplyDisplayTheme()
Expand Down Expand Up @@ -136,9 +134,9 @@ public void ApplyBackgroundAfterPageInit(ShellPage page)

public void ApplyBackgroundAtWindowCreated(MainWindow window)
{
WindowsSystemDispatcherQueueHelper m_wsdqHelper = null;
DesktopAcrylicController m_backdropController = null;
SystemBackdropConfiguration m_configurationSource = null;
WindowsSystemDispatcherQueueHelper? m_wsdqHelper = null;
DesktopAcrylicController? m_backdropController = null;
SystemBackdropConfiguration? m_configurationSource = null;

bool TrySetAcrylicBackdrop(MainWindow window)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/// </summary>
public interface INavigationAware
{
void OnNavigatedTo(object parameter) { }
void OnNavigatedTo(object? parameter) { }

void OnNavigatedFrom() { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public IWindowService ActivateWindow(string key)

// Constructs the window
Type windowType = RegisteredWindows[key].WindowType; // windowType is guaranteed to be a subclass of TWindowBase when the activation service is built
TWindowBase window = (TWindowBase)scope.ServiceProvider.GetService(windowType);
TWindowBase window = (TWindowBase?)scope.ServiceProvider.GetService(windowType)
?? throw new InvalidOperationException($"The window type {windowType} is not registered with the window provider.");

// If the window supports navigation, initialize the navigation service for the window scope
// The navigation service may have been instantiated and injected into 'window' already.
Expand Down
26 changes: 26 additions & 0 deletions Natsurainko.FluentLauncher/Utils/JsonNodeUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.IO;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace Natsurainko.FluentLauncher.Utils;

internal static class JsonNodeUtils
{
public static JsonNode ParseFile(string filePath)
{
JsonNode? jsonNode = null;
try
{
jsonNode = JsonNode.Parse(File.ReadAllText(filePath));
}
catch (JsonException) { }

// jsonNode is null if either
// - the file is not a valid json file, or
// - the file contains the string "null" and JsonNode.Parse(string) returns null.
if (jsonNode is null)
throw new InvalidDataException($"Error in parsing the json file {filePath}.");

return jsonNode;
}
}
12 changes: 12 additions & 0 deletions Natsurainko.FluentLauncher/Utils/LinqExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Linq;

namespace Natsurainko.FluentLauncher.Utils;

internal static class LinqExtensions
{
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source) where T : class
{
return source.Where(x => x is not null)!;
}
}
17 changes: 17 additions & 0 deletions Natsurainko.FluentLauncher/Utils/NavigationViewItemExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Natsurainko.FluentLauncher.Utils;

internal static class NavigationViewItemExtensions
{
public static string GetTag(this NavigationViewItem item)
{
return item.Tag?.ToString()
?? throw new ArgumentNullException("The item's tag is null.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public ActivitiesNavigationViewModel(INavigationService navigationService)

#region Navigation

void INavigationAware.OnNavigatedTo(object parameter)
void INavigationAware.OnNavigatedTo(object? parameter)
{
if (parameter is string pageKey)
_navigationService.NavigateTo(pageKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Natsurainko.FluentLauncher.Utils;
using Nrk.FluentCore.Launch;
using Nrk.FluentCore.Management.Mods;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
Expand Down Expand Up @@ -36,9 +37,10 @@ public CoreModsViewModel(INavigationService navigationService)
[ObservableProperty]
private string searchBoxInput;

void INavigationAware.OnNavigatedTo(object parameter)
void INavigationAware.OnNavigatedTo(object? parameter)
{
var gameInfo = parameter as GameInfo;
if (parameter is not GameInfo gameInfo)
throw new ArgumentException("Invalid parameter type");

SupportMod = gameInfo.IsSupportMod();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Natsurainko.FluentLauncher.Views.Common;
using Nrk.FluentCore.Authentication;
using Nrk.FluentCore.Launch;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
Expand Down Expand Up @@ -40,9 +41,11 @@ public CoreSettingsViewModel(GameService gameService, AccountService accountServ
_accountService = accountService;
}

void INavigationAware.OnNavigatedTo(object parameter)
void INavigationAware.OnNavigatedTo(object? parameter)
{
_gameInfo = parameter as GameInfo;
if (parameter is not GameInfo _gameInfo)
throw new ArgumentException("Invalid parameter type");

GameSpecialConfig = _gameInfo.GetSpecialConfig();

Accounts = _accountService.Accounts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ public string FormatSize

public bool IsVanilla => _gameInfo.IsVanilla;

void INavigationAware.OnNavigatedTo(object parameter)
void INavigationAware.OnNavigatedTo(object? parameter)
{
_gameInfo = parameter as GameInfo;
if (parameter is not GameInfo gameInfo)
throw new ArgumentException("Invalid parameter type");

_gameInfo = gameInfo;

Task.Run(() =>
{
Expand Down
Loading

0 comments on commit d4ae8be

Please sign in to comment.