Files
NebulaLauncher/Nebula.Launcher/ViewModels/ContentBrowserViewModel.cs

398 lines
11 KiB
C#
Raw Normal View History

2025-01-10 12:28:29 +03:00
using System;
2025-01-11 20:39:58 +03:00
using System.Collections.Frozen;
using System.Collections.Generic;
2025-01-10 12:28:29 +03:00
using System.Collections.ObjectModel;
2025-01-12 15:15:01 +03:00
using System.Diagnostics;
2025-01-11 20:39:58 +03:00
using System.Diagnostics.CodeAnalysis;
2025-01-12 15:15:01 +03:00
using System.IO;
2025-01-11 20:39:58 +03:00
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
2025-01-10 12:28:29 +03:00
using CommunityToolkit.Mvvm.ComponentModel;
2025-01-11 20:39:58 +03:00
using Microsoft.Extensions.DependencyInjection;
2025-01-10 12:28:29 +03:00
using Nebula.Launcher.ViewHelper;
using Nebula.Launcher.Views.Pages;
2025-01-11 20:39:58 +03:00
using Nebula.Shared;
2025-01-10 12:28:29 +03:00
using Nebula.Shared.Models;
2025-01-11 20:39:58 +03:00
using Nebula.Shared.Services;
using Nebula.Shared.Utils;
2025-01-10 12:28:29 +03:00
namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(typeof(ContentBrowserView))]
public sealed partial class ContentBrowserViewModel : ViewModelBase
{
2025-01-11 20:39:58 +03:00
private readonly IServiceProvider _provider;
private readonly ContentService _contentService;
private readonly CancellationService _cancellationService;
2025-01-12 15:15:01 +03:00
private readonly FileService _fileService;
2025-01-11 20:39:58 +03:00
private readonly DebugService _debugService;
private readonly PopupMessageService _popupService;
public ObservableCollection<ContentEntry> Entries { get; } = new();
private readonly List<ContentEntry> _root = new();
private List<string> _history = new();
2025-01-10 12:28:29 +03:00
[ObservableProperty] private string _message = "";
[ObservableProperty] private string _searchText = "";
2025-01-11 20:39:58 +03:00
private ContentEntry? _selectedEntry;
public ContentEntry? SelectedEntry
{
get => _selectedEntry;
set
{
2025-01-12 15:15:01 +03:00
if (value is { Item: not null })
{
if (_fileService.ContentFileApi.TryOpen(value.Item.Value.Hash, out var stream))
{
var ext = Path.GetExtension(value.Item.Value.Path);
var myTempFile = Path.Combine(Path.GetTempPath(), "tempie"+ ext);
using(var sw = new FileStream(myTempFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
stream.CopyTo(sw);
}
stream.Dispose();
var startInfo = new ProcessStartInfo(myTempFile)
{
UseShellExecute = true
};
Process.Start(startInfo);
}
return;
}
2025-01-11 20:39:58 +03:00
2025-01-12 15:15:01 +03:00
Entries.Clear();
_selectedEntry = value;
if (value == null) return;
2025-01-11 20:39:58 +03:00
2025-01-12 15:15:01 +03:00
foreach (var (_, entryCh) in value.Childs)
2025-01-11 20:39:58 +03:00
{
Entries.Add(entryCh);
}
}
}
2025-01-10 12:28:29 +03:00
public ContentBrowserViewModel() : base()
{
2025-01-11 20:39:58 +03:00
var a = new ContentEntry(this, "A:", "");
var b = new ContentEntry(this, "B", "");
a.TryAddChild(b);
Entries.Add(a);
}
public ContentBrowserViewModel(IServiceProvider provider, ContentService contentService, CancellationService cancellationService,
FileService fileService, HubService hubService, DebugService debugService, PopupMessageService popupService) : base(provider)
{
_provider = provider;
_contentService = contentService;
_cancellationService = cancellationService;
2025-01-12 15:15:01 +03:00
_fileService = fileService;
2025-01-11 20:39:58 +03:00
_debugService = debugService;
_popupService = popupService;
2025-01-12 15:15:01 +03:00
foreach (var info in hubService.ServerList)
{
_root.Add(new ContentEntry(this, ToContentUrl(info.Address.ToRobustUrl()),info.Address));
}
2025-01-11 20:39:58 +03:00
hubService.HubServerChangedEventArgs += HubServerChangedEventArgs;
hubService.HubServerLoaded += GoHome;
2025-01-12 15:15:01 +03:00
if (!hubService.IsUpdating)
{
GoHome();
}
2025-01-10 12:28:29 +03:00
}
2025-01-11 20:39:58 +03:00
private void GoHome()
2025-01-10 12:28:29 +03:00
{
2025-01-11 20:39:58 +03:00
SelectedEntry = null;
foreach (var entry in _root)
{
Entries.Add(entry);
}
2025-01-10 12:28:29 +03:00
}
2025-01-11 20:39:58 +03:00
private void HubServerChangedEventArgs(HubServerChangedEventArgs obj)
{
if(obj.Action == HubServerChangeAction.Clear) _root.Clear();
if (obj.Action == HubServerChangeAction.Add)
{
foreach (var info in obj.Items)
{
_root.Add(new ContentEntry(this, ToContentUrl(info.Address.ToRobustUrl()),info.Address));
}
};
}
2025-01-10 12:28:29 +03:00
2025-01-12 15:15:01 +03:00
public async void Go(ContentPath path, bool appendHistory = true)
2025-01-10 12:28:29 +03:00
{
2025-01-12 15:15:01 +03:00
if (path.Pathes.Count <= 1)
2025-01-11 20:39:58 +03:00
{
SearchText = "";
GoHome();
return;
}
if (path.Pathes[0] != "content:" || path.Pathes.Count < 3)
{
return;
}
2025-01-12 15:15:01 +03:00
var oriPath = path.Clone();
2025-01-11 20:39:58 +03:00
path.Pathes.RemoveAt(0);
path.Pathes.RemoveAt(0);
var serverUrl = path.Pathes[0];
path.Pathes.RemoveAt(0);
_debugService.Debug(path.Path + " " + serverUrl +" "+ SelectedEntry?.ServerName);
2025-01-10 12:28:29 +03:00
2025-01-11 20:39:58 +03:00
try
{
ContentEntry entry;
if (serverUrl == SelectedEntry?.ServerName)
entry = SelectedEntry.GetRoot();
else
entry = await CreateEntry(serverUrl);
if (!entry.TryGetEntry(path, out var centry))
{
throw new Exception("Not found!");
}
2025-01-12 15:15:01 +03:00
if (appendHistory)
{
AppendHistory(SearchText);
}
SearchText = oriPath.Path;
2025-01-11 20:39:58 +03:00
2025-01-12 15:15:01 +03:00
SelectedEntry = centry;
2025-01-11 20:39:58 +03:00
}
catch (Exception e)
{
Console.WriteLine(e);
2025-01-12 15:15:01 +03:00
SearchText = oriPath.Path;
2025-01-11 20:39:58 +03:00
_popupService.Popup(e);
}
}
public void OnBackEnter()
{
2025-01-12 15:15:01 +03:00
Go(new ContentPath(GetHistory()), false);
2025-01-10 12:28:29 +03:00
}
public void OnGoEnter()
{
2025-01-11 20:39:58 +03:00
Go(new ContentPath(SearchText));
}
private async Task<ContentEntry> CreateEntry(string serverUrl)
{
var rurl = serverUrl.ToRobustUrl();
var info = await _contentService.GetBuildInfo(rurl, _cancellationService.Token);
var loading = _provider.GetService<LoadingContextViewModel>()!;
loading.LoadingName = "Loading entry";
_popupService.Popup(loading);
var items = await _contentService.EnsureItems(info.RobustManifestInfo, loading,
_cancellationService.Token);
var rootEntry = new ContentEntry(this,ToContentUrl(rurl), serverUrl);
foreach (var item in items)
{
var path = new ContentPath(item.Path);
rootEntry.CreateItem(path, item);
}
2025-01-10 12:28:29 +03:00
2025-01-11 20:39:58 +03:00
loading.Dispose();
return rootEntry;
}
private void AppendHistory(string str)
{
if(_history.Count >= 10) _history.RemoveAt(9);
_history.Insert(0, str);
}
private string GetHistory()
{
if (_history.Count == 0) return "";
var h = _history[0];
_history.RemoveAt(0);
return h;
}
private string ToContentUrl(RobustUrl serverUrl)
{
var port = serverUrl.Uri.Port != -1 ? (":"+serverUrl.Uri.Port) : "";
return "content://" + serverUrl.Uri.Host + port;
2025-01-10 12:28:29 +03:00
}
}
2025-01-11 20:39:58 +03:00
public class ContentEntry
2025-01-10 12:28:29 +03:00
{
2025-01-11 20:39:58 +03:00
private readonly ContentBrowserViewModel _viewModel;
public static IImage DirImage = new Bitmap(AssetLoader.Open(new Uri("avares://Nebula.Launcher/Assets/dir.png")));
public static IImage IconImage = new Bitmap(AssetLoader.Open(new Uri("avares://Nebula.Launcher/Assets/file.png")));
public RobustManifestItem? Item;
public bool IsDirectory => Item == null;
public string Name { get; private set; }
public string ServerName { get; private set; }
public IImage IconPath { get; set; } = DirImage;
public ContentEntry? Parent { get; private set; }
public bool IsRoot => Parent == null;
private readonly Dictionary<string, ContentEntry> _childs = new();
public IReadOnlyDictionary<string, ContentEntry> Childs => _childs.ToFrozenDictionary();
public bool TryGetChild(string name,[NotNullWhen(true)] out ContentEntry? child)
{
return _childs.TryGetValue(name, out child);
}
public bool TryAddChild(ContentEntry contentEntry)
{
if(_childs.TryAdd(contentEntry.Name, contentEntry))
{
contentEntry.Parent = this;
return true;
}
return false;
}
internal ContentEntry(ContentBrowserViewModel viewModel, string name, string serverName)
{
Name = name;
ServerName = serverName;
_viewModel = viewModel;
}
public ContentPath GetPath()
{
if (Parent != null)
{
var path = Parent.GetPath();
path.Pathes.Add(Name);
return path;
}
return new ContentPath(Name);
}
public ContentEntry GetOrCreateDirectory(ContentPath rootPath)
{
if (rootPath.Pathes.Count == 0) return this;
var fName = rootPath.Pathes[0];
rootPath.Pathes.RemoveAt(0);
if(!TryGetChild(fName, out var child))
{
child = new ContentEntry(_viewModel, fName, ServerName);
TryAddChild(child);
}
return child.GetOrCreateDirectory(rootPath);
}
public ContentEntry GetRoot()
{
if (Parent == null) return this;
return Parent.GetRoot();
}
public ContentEntry CreateItem(ContentPath path, RobustManifestItem item)
{
var dir = path.GetDirectory();
var dirEntry = GetOrCreateDirectory(dir);
var entry = new ContentEntry(_viewModel, path.GetName(), ServerName)
{
Item = item
};
dirEntry.TryAddChild(entry);
entry.IconPath = IconImage;
return entry;
}
public bool TryGetEntry(ContentPath path, out ContentEntry? entry)
{
entry = null;
if (path.Pathes.Count == 0)
{
entry = this;
return true;
}
var fName = path.Pathes[0];
path.Pathes.RemoveAt(0);
if(!TryGetChild(fName, out var child))
{
return false;
}
return child.TryGetEntry(path, out entry);
}
public void OnPathGo()
{
_viewModel.Go(GetPath());
}
2025-01-10 12:28:29 +03:00
}
2025-01-11 20:39:58 +03:00
public struct ContentPath
2025-01-10 12:28:29 +03:00
{
2025-01-11 20:39:58 +03:00
public List<string> Pathes;
public ContentPath(List<string> pathes)
{
Pathes = pathes;
}
public ContentPath(string path)
{
Pathes = path.Split("/").ToList();
}
public ContentPath GetDirectory()
{
var p = Pathes.ToList();
p.RemoveAt(Pathes.Count - 1);
return new ContentPath(p);
}
public string GetName()
{
return Pathes.Last();
}
2025-01-12 15:15:01 +03:00
public ContentPath Clone()
{
return new ContentPath(Pathes.ToList());
}
2025-01-11 20:39:58 +03:00
public string Path => string.Join("/", Pathes);
}