Skip to content

Commit 03b4c4f

Browse files
committed
Add new Helpers + fix WhatsNewDisplayService
taken from CommunityToolkit/Windows#600
1 parent 31532f1 commit 03b4c4f

File tree

4 files changed

+680
-6
lines changed

4 files changed

+680
-6
lines changed

FluentWeather/FluentWeather.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
3838
<PackageReference Include="CommunityToolkit.Uwp.Controls.Primitives" Version="8.2.251219" />
3939
<PackageReference Include="CommunityToolkit.Uwp.Controls.Segmented" Version="8.2.251219" />
40+
<PackageReference Include="CommunityToolkit.Uwp.Helpers" Version="8.2.251219" />
4041
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.5" />
4142
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
4243
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />

FluentWeather/Services/WhatsNewDisplayService.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Windows.ApplicationModel.Core;
44
using Windows.UI.Core;
55
using FluentWeather.Dialogs;
6+
using CommunityToolkit.Uwp.Helpers;
67

78
namespace FluentWeather.Services
89
{
@@ -16,12 +17,12 @@ internal static async Task ShowIfAppropriateAsync()
1617
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
1718
CoreDispatcherPriority.Normal, async () =>
1819
{
19-
//if (SystemInformation.Instance.IsAppUpdated && !shown)
20-
//{
21-
// shown = true;
22-
// var dialog = new WhatsNewDialog();
23-
// await dialog.ShowAsync();
24-
//}
20+
if (SystemInformation.Instance.IsAppUpdated && !shown)
21+
{
22+
shown = true;
23+
var dialog = new WhatsNewDialog();
24+
await dialog.ShowAsync();
25+
}
2526
});
2627
}
2728
}
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Threading.Tasks;
10+
using CommunityToolkit.Common.Helpers;
11+
using Windows.Storage;
12+
using Windows.System;
13+
using CommunityToolkit.Helpers;
14+
15+
namespace CommunityToolkit.Uwp.Helpers.ObjectStorage;
16+
17+
/// <summary>
18+
/// Storage helper for files and folders living in Windows.Storage.ApplicationData storage endpoints.
19+
/// </summary>
20+
/// <param name="appData">The data store to interact with.</param>
21+
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="SystemSerializer"/>.</param>
22+
public partial class ApplicationDataStorageHelper(ApplicationData appData, IObjectSerializer? objectSerializer = null) : ISettingsStorageHelper<string>
23+
{
24+
/// <summary>
25+
/// Gets the settings container.
26+
/// </summary>
27+
public ApplicationDataContainer Settings => AppData.LocalSettings;
28+
29+
/// <summary>
30+
/// Gets the storage folder.
31+
/// </summary>
32+
public StorageFolder Folder => AppData.LocalFolder;
33+
34+
/// <summary>
35+
/// Gets the storage host.
36+
/// </summary>
37+
protected ApplicationData AppData { get; } = appData ?? throw new ArgumentNullException(nameof(appData));
38+
39+
/// <summary>
40+
/// Gets the serializer for converting stored values.
41+
/// </summary>
42+
protected IObjectSerializer Serializer { get; } = objectSerializer ?? new SystemSerializer();
43+
44+
/// <summary>
45+
/// Get a new instance using ApplicationData.Current and the provided serializer.
46+
/// </summary>
47+
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="SystemSerializer"/>.</param>
48+
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
49+
public static ApplicationDataStorageHelper GetCurrent(IObjectSerializer? objectSerializer = null)
50+
{
51+
var appData = ApplicationData.Current;
52+
return new ApplicationDataStorageHelper(appData, objectSerializer);
53+
}
54+
55+
#if !HAS_UNO
56+
/// <summary>
57+
/// Get a new instance using the ApplicationData for the provided user and serializer.
58+
/// </summary>
59+
/// <param name="user">App data user owner.</param>
60+
/// <param name="objectSerializer">Serializer for converting stored values. Defaults to <see cref="SystemSerializer"/>.</param>
61+
/// <returns>A new instance of ApplicationDataStorageHelper.</returns>
62+
public static async Task<ApplicationDataStorageHelper> GetForUserAsync(User user, IObjectSerializer? objectSerializer = null)
63+
{
64+
var appData = await ApplicationData.GetForUserAsync(user);
65+
return new ApplicationDataStorageHelper(appData, objectSerializer);
66+
}
67+
#endif
68+
69+
/// <summary>
70+
/// Determines whether a setting already exists.
71+
/// </summary>
72+
/// <param name="key">Key of the setting (that contains object).</param>
73+
/// <returns>True if a value exists.</returns>
74+
public bool KeyExists(string key)
75+
{
76+
return Settings.Values.ContainsKey(key);
77+
}
78+
79+
/// <summary>
80+
/// Retrieves a single item by its key.
81+
/// </summary>
82+
/// <typeparam name="T">Type of object retrieved.</typeparam>
83+
/// <param name="key">Key of the object.</param>
84+
/// <param name="default">Default value of the object.</param>
85+
/// <returns>The TValue object.</returns>
86+
public T? Read<T>(string key, T? @default = default)
87+
{
88+
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
89+
{
90+
return Serializer.Deserialize<T>(valueString);
91+
}
92+
93+
return @default;
94+
}
95+
96+
/// <inheritdoc />
97+
public bool TryRead<T>(string key, out T? value)
98+
{
99+
if (Settings.Values.TryGetValue(key, out var valueObj) && valueObj is string valueString)
100+
{
101+
value = Serializer.Deserialize<T>(valueString);
102+
return true;
103+
}
104+
105+
value = default;
106+
return false;
107+
}
108+
109+
/// <inheritdoc />
110+
public void Save<T>(string key, T value)
111+
{
112+
Settings.Values[key] = Serializer.Serialize(value);
113+
}
114+
115+
/// <inheritdoc />
116+
public bool TryDelete(string key)
117+
{
118+
return Settings.Values.Remove(key);
119+
}
120+
121+
/// <inheritdoc />
122+
public void Clear()
123+
{
124+
Settings.Values.Clear();
125+
}
126+
127+
/// <summary>
128+
/// Determines whether a setting already exists in composite.
129+
/// </summary>
130+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
131+
/// <param name="key">Key of the setting (that contains object).</param>
132+
/// <returns>True if a value exists.</returns>
133+
public bool KeyExists(string compositeKey, string key)
134+
{
135+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
136+
{
137+
return composite.ContainsKey(key);
138+
}
139+
140+
return false;
141+
}
142+
143+
/// <summary>
144+
/// Attempts to retrieve a single item by its key in composite.
145+
/// </summary>
146+
/// <typeparam name="T">Type of object retrieved.</typeparam>
147+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
148+
/// <param name="key">Key of the object.</param>
149+
/// <param name="value">The value of the object retrieved.</param>
150+
/// <returns>The T object.</returns>
151+
public bool TryRead<T>(string compositeKey, string key, out T? value)
152+
{
153+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
154+
{
155+
string compositeValue = (string)composite[key];
156+
if (compositeValue != null)
157+
{
158+
value = Serializer.Deserialize<T>(compositeValue);
159+
return true;
160+
}
161+
}
162+
163+
value = default;
164+
return false;
165+
}
166+
167+
/// <summary>
168+
/// Retrieves a single item by its key in composite.
169+
/// </summary>
170+
/// <typeparam name="T">Type of object retrieved.</typeparam>
171+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
172+
/// <param name="key">Key of the object.</param>
173+
/// <param name="default">Default value of the object.</param>
174+
/// <returns>The T object.</returns>
175+
public T? Read<T>(string compositeKey, string key, T? @default = default)
176+
{
177+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
178+
{
179+
if (composite.TryGetValue(key, out var valueObj) && valueObj is string value)
180+
{
181+
return Serializer.Deserialize<T>(value);
182+
}
183+
}
184+
185+
return @default;
186+
}
187+
188+
/// <summary>
189+
/// Saves a group of items by its key in a composite.
190+
/// This method should be considered for objects that do not exceed 8k bytes during the lifetime of the application
191+
/// and for groups of settings which need to be treated in an atomic way.
192+
/// </summary>
193+
/// <typeparam name="T">Type of object saved.</typeparam>
194+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
195+
/// <param name="values">Objects to save.</param>
196+
public void Save<T>(string compositeKey, IDictionary<string, T> values)
197+
{
198+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
199+
{
200+
foreach (KeyValuePair<string, T> setting in values)
201+
{
202+
var serializedValue = Serializer.Serialize(setting.Value) ?? string.Empty;
203+
if (composite.ContainsKey(setting.Key))
204+
{
205+
composite[setting.Key] = serializedValue;
206+
}
207+
else
208+
{
209+
composite.Add(setting.Key, serializedValue);
210+
}
211+
}
212+
}
213+
else
214+
{
215+
composite = [];
216+
foreach (KeyValuePair<string, T> setting in values)
217+
{
218+
var serializedValue = Serializer.Serialize(setting.Value) ?? string.Empty;
219+
composite.Add(setting.Key, serializedValue);
220+
}
221+
222+
Settings.Values[compositeKey] = composite;
223+
}
224+
}
225+
226+
/// <summary>
227+
/// Deletes a single item by its key in composite.
228+
/// </summary>
229+
/// <param name="compositeKey">Key of the composite (that contains settings).</param>
230+
/// <param name="key">Key of the object.</param>
231+
/// <returns>A boolean indicator of success.</returns>
232+
public bool TryDelete(string compositeKey, string key)
233+
{
234+
if (TryRead(compositeKey, out ApplicationDataCompositeValue? composite) && composite != null)
235+
{
236+
return composite.Remove(key);
237+
}
238+
239+
return false;
240+
}
241+
242+
/// <inheritdoc />
243+
public Task<IEnumerable<(DirectoryItemType ItemType, string Name)>> ReadFolderAsync(string folderPath)
244+
{
245+
return ReadFolderAsync(Folder, folderPath);
246+
}
247+
248+
249+
/// <inheritdoc />
250+
public Task CreateFolderAsync(string folderPath)
251+
{
252+
return CreateFolderAsync(Folder, folderPath);
253+
}
254+
255+
/// <inheritdoc />
256+
public Task<bool> TryDeleteItemAsync(string itemPath)
257+
{
258+
return TryDeleteItemAsync(Folder, itemPath);
259+
}
260+
261+
/// <inheritdoc />
262+
public Task<bool> TryRenameItemAsync(string itemPath, string newName)
263+
{
264+
return TryRenameItemAsync(Folder, itemPath, newName);
265+
}
266+
267+
private async Task<IEnumerable<(DirectoryItemType, string)>> ReadFolderAsync(StorageFolder folder, string folderPath)
268+
{
269+
var targetFolder = await folder.GetFolderAsync(NormalizePath(folderPath));
270+
var items = await targetFolder.GetItemsAsync();
271+
272+
return items.Select((item) =>
273+
{
274+
var itemType = item.IsOfType(StorageItemTypes.File) ? DirectoryItemType.File
275+
: item.IsOfType(StorageItemTypes.Folder) ? DirectoryItemType.Folder
276+
: DirectoryItemType.None;
277+
278+
return (itemType, item.Name);
279+
});
280+
}
281+
282+
private async Task CreateFolderAsync(StorageFolder folder, string folderPath)
283+
{
284+
await folder.CreateFolderAsync(NormalizePath(folderPath), CreationCollisionOption.OpenIfExists);
285+
}
286+
287+
private async Task<bool> TryDeleteItemAsync(StorageFolder folder, string itemPath)
288+
{
289+
try
290+
{
291+
var item = await folder.GetItemAsync(NormalizePath(itemPath));
292+
await item.DeleteAsync();
293+
return true;
294+
}
295+
catch
296+
{
297+
return false;
298+
}
299+
}
300+
301+
private async Task<bool> TryRenameItemAsync(StorageFolder folder, string itemPath, string newName)
302+
{
303+
try
304+
{
305+
var item = await folder.GetItemAsync(NormalizePath(itemPath));
306+
await item.RenameAsync(newName, NameCollisionOption.FailIfExists);
307+
return true;
308+
}
309+
catch
310+
{
311+
return false;
312+
}
313+
}
314+
315+
private string NormalizePath(string path)
316+
{
317+
var directoryName = Path.GetDirectoryName(path) ?? string.Empty;
318+
var fileName = Path.GetFileName(path);
319+
return Path.Combine(directoryName, fileName);
320+
}
321+
}

0 commit comments

Comments
 (0)