DI 注入文件系统,web平台不支持System.IO.File
parent
a02aeca6df
commit
09d8bc7ba8
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Notes.Android;
|
||||||
|
|
||||||
|
class FileService : IFileService
|
||||||
|
{
|
||||||
|
public string Combine(string args0, string args1)
|
||||||
|
{
|
||||||
|
return Path.Combine(args0, args1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetRandomFileName()
|
||||||
|
{
|
||||||
|
return Path.GetRandomFileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> EnumerateFiles(string path, string patten)
|
||||||
|
{
|
||||||
|
return Directory.EnumerateFiles(path, patten);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFileName(string filename)
|
||||||
|
{
|
||||||
|
return Path.GetFileName(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ReadAllText(string filename)
|
||||||
|
{
|
||||||
|
return File.ReadAllText(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime GetLastWriteTime(string filename)
|
||||||
|
{
|
||||||
|
return File.GetLastWriteTime(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ExistsFile(string filename)
|
||||||
|
{
|
||||||
|
return File.Exists(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(string path)
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteAllText(string path, string text)
|
||||||
|
{
|
||||||
|
File.WriteAllText(path, text);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using Android.Content.PM;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Android;
|
using Avalonia.Android;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using Notes.ViewModels;
|
||||||
|
|
||||||
namespace Notes.Android;
|
namespace Notes.Android;
|
||||||
|
|
||||||
|
@ -16,6 +17,10 @@ public class MainActivity : AvaloniaMainActivity<App>
|
||||||
{
|
{
|
||||||
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
|
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
var services = new Services();
|
||||||
|
services.Register<IFileService>(new FileService());
|
||||||
|
App.services = services;
|
||||||
return base.CustomizeAppBuilder(builder)
|
return base.CustomizeAppBuilder(builder)
|
||||||
.WithInterFont()
|
.WithInterFont()
|
||||||
.UseReactiveUI();
|
.UseReactiveUI();
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
|
<RuntimeIdentifier>browser-wasm</RuntimeIdentifier>
|
||||||
<WasmMainJSPath>AppBundle\main.js</WasmMainJSPath>
|
<WasmMainJSPath>AppBundle\main.js</WasmMainJSPath>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
|
<WasmAllowUndefinedSymbols>true</WasmAllowUndefinedSymbols>
|
||||||
|
<MSBuildDebugEngine>1</MSBuildDebugEngine>
|
||||||
|
<WasmBuildNative>true</WasmBuildNative>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Notes.Desktop;
|
||||||
|
|
||||||
|
class FileService: IFileService
|
||||||
|
{
|
||||||
|
public string Combine(string args0, string args1)
|
||||||
|
{
|
||||||
|
return Path.Combine(args0, args1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetRandomFileName()
|
||||||
|
{
|
||||||
|
return Path.GetRandomFileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> EnumerateFiles(string path, string patten)
|
||||||
|
{
|
||||||
|
return Directory.EnumerateFiles(path,patten);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFileName(string filename)
|
||||||
|
{
|
||||||
|
return Path.GetFileName(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ReadAllText(string filename)
|
||||||
|
{
|
||||||
|
return File.ReadAllText(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime GetLastWriteTime(string filename)
|
||||||
|
{
|
||||||
|
return File.GetLastWriteTime(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ExistsFile(string filename)
|
||||||
|
{
|
||||||
|
return File.Exists(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(string path)
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteAllText(string path, string text)
|
||||||
|
{
|
||||||
|
File.WriteAllText(path,text);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
|
using Notes.ViewModels;
|
||||||
|
|
||||||
namespace Notes.Desktop;
|
namespace Notes.Desktop;
|
||||||
|
|
||||||
|
@ -10,8 +11,14 @@ sealed class Program
|
||||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||||
// yet and stuff might break.
|
// yet and stuff might break.
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
public static void Main(string[] args)
|
||||||
.StartWithClassicDesktopLifetime(args);
|
{
|
||||||
|
var services = new Services();
|
||||||
|
services.Register<IFileService>(new FileService());
|
||||||
|
App.services = services;
|
||||||
|
BuildAvaloniaApp()
|
||||||
|
.StartWithClassicDesktopLifetime(args);
|
||||||
|
}
|
||||||
|
|
||||||
// Avalonia configuration, don't remove; also used by visual designer.
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
public static AppBuilder BuildAvaloniaApp()
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
|
|
|
@ -8,9 +8,12 @@ namespace Notes;
|
||||||
|
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
|
public static IServices services { get; set; }
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
FileSystem.fileService = services.GetSafeAs<IFileService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Notes;
|
||||||
|
|
||||||
|
public interface IFileService
|
||||||
|
{
|
||||||
|
string Combine(string args0, string args1);
|
||||||
|
string GetRandomFileName();
|
||||||
|
IEnumerable<string> EnumerateFiles(string path, string patten);
|
||||||
|
string GetFileName(string filename);
|
||||||
|
string ReadAllText(string filename);
|
||||||
|
DateTime GetLastWriteTime(string filename);
|
||||||
|
bool ExistsFile(string filename);
|
||||||
|
void Delete(string path);
|
||||||
|
void WriteAllText(string path, string text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FileSystem
|
||||||
|
{
|
||||||
|
public static IFileService fileService;
|
||||||
|
|
||||||
|
public static string Combine(string args0, string args1)
|
||||||
|
{
|
||||||
|
return fileService.Combine(args0, args1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetRandomFileName()
|
||||||
|
{
|
||||||
|
return fileService.GetRandomFileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> EnumerateFiles(string path, string patten)
|
||||||
|
{
|
||||||
|
return fileService.EnumerateFiles(path, patten);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetFileName(string filename)
|
||||||
|
{
|
||||||
|
return fileService.GetFileName(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadAllText(string filename)
|
||||||
|
{
|
||||||
|
return fileService.ReadAllText(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime GetLastWriteTime(string filename)
|
||||||
|
{
|
||||||
|
return fileService.GetLastWriteTime(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ExistsFile(string filename)
|
||||||
|
{
|
||||||
|
return fileService.ExistsFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Delete(string filename)
|
||||||
|
{
|
||||||
|
fileService.Delete(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteAllText(string path, string text)
|
||||||
|
{
|
||||||
|
fileService.WriteAllText(path, text);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Utils;
|
using Utils;
|
||||||
|
|
||||||
|
@ -20,37 +19,37 @@ public class Note
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var appDataDirectory = Path.Combine(AppContext.BaseDirectory, "Data");
|
var appDataDirectory = FileSystem.Combine(AppContext.BaseDirectory, "Data");
|
||||||
IOUtils.EnsureDirectory(appDataDirectory,false);
|
IOUtils.EnsureDirectory(appDataDirectory,false);
|
||||||
return appDataDirectory;
|
return appDataDirectory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Note()
|
public Note()
|
||||||
{
|
{
|
||||||
Filename = $"{Path.GetRandomFileName()}{Extension}";
|
Filename = $"{FileSystem.GetRandomFileName()}{Extension}";
|
||||||
Date = DateTime.Now;
|
Date = DateTime.Now;
|
||||||
Text = "";
|
Text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save() =>
|
public void Save() =>
|
||||||
File.WriteAllText(Path.Combine(AppDataDirectory, Filename), Text);
|
FileSystem.WriteAllText(FileSystem.Combine(AppDataDirectory, Filename), Text);
|
||||||
|
|
||||||
public void Delete() =>
|
public void Delete() =>
|
||||||
File.Delete(Path.Combine(AppDataDirectory, Filename));
|
FileSystem.Delete(FileSystem.Combine(AppDataDirectory, Filename));
|
||||||
|
|
||||||
public static Note Load(string filename)
|
public static Note Load(string filename)
|
||||||
{
|
{
|
||||||
filename = Path.Combine(AppDataDirectory, filename);
|
filename = FileSystem.Combine(AppDataDirectory, filename);
|
||||||
|
|
||||||
if (!File.Exists(filename))
|
if (!FileSystem.ExistsFile(filename))
|
||||||
throw new FileNotFoundException("Unable to find file on local storage.", filename);
|
throw new InvalidOperationException($"Unable to find file '{filename}' on local storage.");
|
||||||
|
|
||||||
return
|
return
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Filename = Path.GetFileName(filename),
|
Filename = FileSystem.GetFileName(filename),
|
||||||
Text = File.ReadAllText(filename),
|
Text = FileSystem.ReadAllText(filename),
|
||||||
Date = File.GetLastWriteTime(filename)
|
Date = FileSystem.GetLastWriteTime(filename)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,13 +59,13 @@ public class Note
|
||||||
string appDataPath = AppDataDirectory;
|
string appDataPath = AppDataDirectory;
|
||||||
|
|
||||||
// Use Linq extensions to load the *.notes.txt files.
|
// Use Linq extensions to load the *.notes.txt files.
|
||||||
return Directory
|
return FileSystem
|
||||||
|
|
||||||
// Select the file names from the directory
|
// Select the file names from the directory
|
||||||
.EnumerateFiles(appDataPath, "*.notes.txt")
|
.EnumerateFiles(appDataPath, "*.notes.txt")
|
||||||
|
|
||||||
// Each file name is used to load a note
|
// Each file name is used to load a note
|
||||||
.Select(filename => Load(Path.GetFileName(filename)))
|
.Select(filename => Load(FileSystem.GetFileName(filename)))
|
||||||
|
|
||||||
// With the final collection of notes, order them by date
|
// With the final collection of notes, order them by date
|
||||||
.OrderByDescending(note => note.Date);
|
.OrderByDescending(note => note.Date);
|
||||||
|
|
|
@ -14,7 +14,7 @@ public partial class AboutViewModel:ViewModelBase
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public void ShowAllNotes()
|
public void ShowAllNotes()
|
||||||
{
|
{
|
||||||
_services.GetAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
|
_services.GetSafeAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Used by designer",true)]
|
[Obsolete("Used by designer",true)]
|
||||||
|
|
|
@ -26,7 +26,7 @@ public partial class AllNotesViewModel: ViewModelBase
|
||||||
{
|
{
|
||||||
var note = new Note();
|
var note = new Note();
|
||||||
note.Filename = string.Empty;
|
note.Filename = string.Empty;
|
||||||
this._services.GetAs<MainViewModel>().Navitation<NoteViewModel>(this._services,this,note);
|
this._services.GetSafeAs<MainViewModel>().Navitation<NoteViewModel>(this._services,this,note);
|
||||||
}
|
}
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public void DeleteNote(ISelectionModel selectionModel)
|
public void DeleteNote(ISelectionModel selectionModel)
|
||||||
|
@ -43,7 +43,7 @@ public partial class AllNotesViewModel: ViewModelBase
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public void OpenAbout()
|
public void OpenAbout()
|
||||||
{
|
{
|
||||||
this._services.GetAs<MainViewModel>().Navitation<AboutViewModel>(this._services);
|
this._services.GetSafeAs<MainViewModel>().Navitation<AboutViewModel>(this._services);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("Used by designer",true)]
|
[Obsolete("Used by designer",true)]
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Notes.ViewModels;
|
||||||
|
|
||||||
|
public interface IServices
|
||||||
|
{
|
||||||
|
void Register<T>(T t);
|
||||||
|
T? Get<T>();
|
||||||
|
T GetSafeAs<T>();
|
||||||
|
}
|
|
@ -1,40 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Utils;
|
using Utils;
|
||||||
|
|
||||||
namespace Notes.ViewModels;
|
namespace Notes.ViewModels;
|
||||||
|
|
||||||
public interface IServices
|
|
||||||
{
|
|
||||||
void Register<T>(T t);
|
|
||||||
T? Get<T>();
|
|
||||||
T GetAs<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Sevices:IServices
|
|
||||||
{
|
|
||||||
private readonly Dictionary<Type, object?> map = new Dictionary<Type, object?>();
|
|
||||||
public void Register<T>(T t)
|
|
||||||
{
|
|
||||||
map.Add(typeof(T), t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? Get<T>()
|
|
||||||
{
|
|
||||||
map.TryGetValue(typeof(T), out var t);
|
|
||||||
return (T?)t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetAs<T>()
|
|
||||||
{
|
|
||||||
T? foo = this.Get<T?>();
|
|
||||||
if (foo is { } t)
|
|
||||||
return t;
|
|
||||||
throw new InvalidOperationException($"Not find type '{typeof(T)}'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public partial class MainViewModel:ViewModelBase
|
public partial class MainViewModel:ViewModelBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -47,7 +17,7 @@ public partial class MainViewModel:ViewModelBase
|
||||||
|
|
||||||
public MainViewModel()
|
public MainViewModel()
|
||||||
{
|
{
|
||||||
_service = new Sevices();
|
_service = App.services;
|
||||||
_service.Register(this);
|
_service.Register(this);
|
||||||
_contentViewModel = new AboutViewModel(_service);
|
_contentViewModel = new AboutViewModel(_service);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,13 +51,13 @@ public partial class NoteViewModel : ViewModelBase
|
||||||
|
|
||||||
public void Back()
|
public void Back()
|
||||||
{
|
{
|
||||||
_services.GetAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
|
_services.GetSafeAs<MainViewModel>().Navitation<AllNotesViewModel>(this._services);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
public void OpenNote()
|
public void OpenNote()
|
||||||
{
|
{
|
||||||
this._services.GetAs<MainViewModel>().Navitation<NoteViewModel>(this._services, this.AllNotesViewModel, this.Note);
|
this._services.GetSafeAs<MainViewModel>().Navitation<NoteViewModel>(this._services, this.AllNotesViewModel, this.Note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Notes.ViewModels;
|
||||||
|
|
||||||
|
public sealed class Services:IServices
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Type, object?> map = new Dictionary<Type, object?>();
|
||||||
|
public void Register<T>(T t)
|
||||||
|
{
|
||||||
|
this.map.Add(typeof(T), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? Get<T>()
|
||||||
|
{
|
||||||
|
this.map.TryGetValue(typeof(T), out var t);
|
||||||
|
return (T?)t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetSafeAs<T>()
|
||||||
|
{
|
||||||
|
T? foo = this.Get<T?>();
|
||||||
|
if (foo is { } t)
|
||||||
|
return t;
|
||||||
|
throw new InvalidOperationException($"Not find type '{typeof(T)}'");
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,6 @@ public partial class MainView : UserControl
|
||||||
{
|
{
|
||||||
public MainView()
|
public MainView()
|
||||||
{
|
{
|
||||||
DataContext = new MainViewModel();
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue