Skip to content

Commit 41f41ff

Browse files
jjw24TBM13
authored andcommitted
Merge pull request Flow-Launcher#3898 from Flow-Launcher/storage_api_method
1 parent 137fb4e commit 41f41ff

File tree

3 files changed

+81
-41
lines changed

3 files changed

+81
-41
lines changed

Flow.Launcher.Infrastructure/Image/ImageLoader.cs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ public static class ImageLoader
1717
private static readonly string ClassName = nameof(ImageLoader);
1818

1919
private static readonly ImageCache ImageCache = new();
20-
private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1);
20+
private static Lock storageLock { get; } = new();
2121
private static BinaryStorage<List<(string, bool)>> _storage;
2222
private static readonly ConcurrentDictionary<string, string> GuidToKey = new();
2323
private static IImageHashGenerator _hashGenerator;
2424
private static readonly bool EnableImageHash = true;
25-
public static ImageSource Image { get; } = new BitmapImage(new Uri(Constant.ImageIcon));
26-
public static ImageSource MissingImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
27-
public static ImageSource LoadingImage { get; } = new BitmapImage(new Uri(Constant.LoadingImgIcon));
25+
public static ImageSource Image => ImageCache[Constant.ImageIcon, false];
26+
public static ImageSource MissingImage => ImageCache[Constant.MissingImgIcon, false];
27+
public static ImageSource LoadingImage => ImageCache[Constant.LoadingImgIcon, false];
2828
public const int SmallIconSize = 64;
2929
public const int FullIconSize = 256;
3030
public const int FullImageSize = 320;
@@ -34,31 +34,29 @@ public static class ImageLoader
3434

3535
public static async Task InitializeAsync()
3636
{
37-
_storage = new BinaryStorage<List<(string, bool)>>("Image");
38-
_hashGenerator = new ImageHashGenerator();
37+
await Task.Run(() =>
38+
{
39+
_storage = new BinaryStorage<List<(string, bool)>>("Image");
40+
_hashGenerator = new ImageHashGenerator();
3941

40-
// Even though we no longer do image preloading and thus don't need _storage,
41-
// for some reason MemoryPackSerializer exceptions appear when this is removed
42-
await LoadStorageToConcurrentDictionaryAsync();
42+
// Even though we no longer do image preloading and thus don't need _storage,
43+
// for some reason MemoryPackSerializer exceptions appear when this is removed
44+
LoadStorageToConcurrentDictionary();
4345

44-
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
45-
{
46-
ImageSource img = new BitmapImage(new Uri(icon));
47-
img.Freeze();
48-
ImageCache[icon, false] = img;
49-
}
46+
foreach (var icon in new[] { Constant.DefaultIcon, Constant.ImageIcon, Constant.MissingImgIcon, Constant.LoadingImgIcon })
47+
{
48+
ImageSource img = new BitmapImage(new Uri(icon));
49+
img.Freeze();
50+
ImageCache[icon, false] = img;
51+
}
52+
});
5053
}
5154

52-
private static async Task<List<(string, bool)>> LoadStorageToConcurrentDictionaryAsync()
55+
private static List<(string, bool)> LoadStorageToConcurrentDictionary()
5356
{
54-
await storageLock.WaitAsync();
55-
try
56-
{
57-
return await _storage.TryLoadAsync(new List<(string, bool)>());
58-
}
59-
finally
57+
lock (storageLock)
6058
{
61-
storageLock.Release();
59+
return _storage.TryLoad([]);
6260
}
6361
}
6462

@@ -109,7 +107,7 @@ private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool
109107
{
110108
Log.Error(ClassName, $"Failed to load image from path {path}: Remote images are not supported.");
111109

112-
ImageSource image = ImageCache[Constant.MissingImgIcon, false];
110+
ImageSource image = MissingImage;
113111
ImageCache[path, false] = image;
114112
imageResult = new ImageResult(image, ImageType.Error);
115113
}
@@ -137,7 +135,7 @@ private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool
137135
Log.Exception(ClassName, $"Failed to get thumbnail for {path} on first try", e);
138136
Log.Exception(ClassName, $"Failed to get thumbnail for {path} on second try", e2);
139137

140-
ImageSource image = ImageCache[Constant.MissingImgIcon, false];
138+
ImageSource image = MissingImage;
141139
ImageCache[path, false] = image;
142140
imageResult = new ImageResult(image, ImageType.Error);
143141
}
@@ -205,7 +203,7 @@ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImag
205203
}
206204
else
207205
{
208-
image = ImageCache[Constant.MissingImgIcon, false];
206+
image = MissingImage;
209207
path = Constant.MissingImgIcon;
210208
}
211209

Flow.Launcher.Infrastructure/Storage/BinaryStorage.cs

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace Flow.Launcher.Infrastructure.Storage
1313
{
1414
/// <summary>
15-
/// Stroage object using binary data
15+
/// Storage object using binary data
1616
/// Normally, it has better performance, but not readable
1717
/// </summary>
1818
/// <remarks>
@@ -53,6 +53,45 @@ public BinaryStorage(string filename, string directoryPath = null!)
5353
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
5454
}
5555

56+
public T TryLoad(T defaultData)
57+
{
58+
if (Data != null) return Data;
59+
60+
if (File.Exists(FilePath))
61+
{
62+
if (new FileInfo(FilePath).Length == 0)
63+
{
64+
Log.Error(ClassName, $"Zero length cache file <{FilePath}>");
65+
Data = defaultData;
66+
Save();
67+
}
68+
69+
var bytes = File.ReadAllBytes(FilePath);
70+
Data = Deserialize(bytes, defaultData);
71+
}
72+
else
73+
{
74+
Log.Info(ClassName, "Cache file not exist, load default data");
75+
Data = defaultData;
76+
Save();
77+
}
78+
return Data;
79+
}
80+
81+
private T Deserialize(ReadOnlySpan<byte> bytes, T defaultData)
82+
{
83+
try
84+
{
85+
var t = MemoryPackSerializer.Deserialize<T>(bytes);
86+
return t ?? defaultData;
87+
}
88+
catch (System.Exception e)
89+
{
90+
Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e);
91+
return defaultData;
92+
}
93+
}
94+
5695
public async ValueTask<T> TryLoadAsync(T defaultData)
5796
{
5897
if (Data != null) return Data;
@@ -79,26 +118,31 @@ public async ValueTask<T> TryLoadAsync(T defaultData)
79118
return Data;
80119
}
81120

82-
private static async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
121+
private async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
83122
{
84123
try
85124
{
86125
var t = await MemoryPackSerializer.DeserializeAsync<T>(stream);
87126
return t ?? defaultData;
88127
}
89-
catch (System.Exception)
128+
catch (System.Exception e)
90129
{
91-
// Log.Exception($"|BinaryStorage.Deserialize|Deserialize error for file <{FilePath}>", e);
130+
Log.Exception(ClassName, $"Deserialize error for file <{FilePath}>", e);
92131
return defaultData;
93132
}
94133
}
95134

96135
public void Save()
136+
{
137+
Save(Data.NonNull());
138+
}
139+
140+
public void Save(T data)
97141
{
98142
// User may delete the directory, so we need to check it
99143
FilesFolders.ValidateDirectory(DirectoryPath);
100144

101-
var serialized = MemoryPackSerializer.Serialize(Data);
145+
var serialized = MemoryPackSerializer.Serialize(data);
102146
File.WriteAllBytes(FilePath, serialized);
103147
}
104148

@@ -107,15 +151,6 @@ public async ValueTask SaveAsync()
107151
await SaveAsync(Data.NonNull());
108152
}
109153

110-
// ImageCache need to convert data into concurrent dictionary for usage,
111-
// so we would better to clear the data
112-
public void ClearData()
113-
{
114-
Data = default;
115-
}
116-
117-
// ImageCache storages data in its class,
118-
// so we need to pass it to SaveAsync
119154
public async ValueTask SaveAsync(T data)
120155
{
121156
// User may delete the directory, so we need to check it
@@ -124,5 +159,12 @@ public async ValueTask SaveAsync(T data)
124159
await using var stream = new FileStream(FilePath, FileMode.Create);
125160
await MemoryPackSerializer.SerializeAsync(stream, data);
126161
}
162+
163+
// ImageCache need to convert data into concurrent dictionary for usage,
164+
// so we would better to clear the data
165+
public void ClearData()
166+
{
167+
Data = default;
168+
}
127169
}
128170
}

Flow.Launcher/PublicAPIInstance.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public void ChangeQuery(string query, bool requery = false)
6868
}
6969

7070
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "<Pending>")]
71-
public async void RestartApp()
71+
public void RestartApp()
7272
{
7373
_mainVM.Hide();
7474

0 commit comments

Comments
 (0)