Skip to content

Commit

Permalink
Merge pull request #13 from felipebaltazar/feature/improve-usage
Browse files Browse the repository at this point in the history
Usage improvements
  • Loading branch information
felipebaltazar authored Feb 18, 2024
2 parents e401073 + d6b9629 commit e22a86e
Show file tree
Hide file tree
Showing 17 changed files with 531 additions and 343 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,4 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml
samples/Maui.ServerDrivenUI.ApiSample/Properties/ServiceDependencies/**
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IServerDrivenUISettings

TimeSpan UIElementCacheExpiration { get; set; }

void RegisterElementGetter(Func<string, Task<ServerUIElement>> UiElementGetter);
void RegisterElementGetter(Func<string, IServiceProvider, Task<ServerUIElement>> UiElementGetter);

void AddServerElement(string key);
}
2 changes: 1 addition & 1 deletion Maui.ServerDrivenUI/AppBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private static void ConfigureDb(LiteDBOptions config, ServerDrivenUISettings set

private static bool InitServerDrivenUIService()
{
var service = Application.Current!.Handler.MauiContext!.Services.GetService<IServerDrivenUIService>();
var service = ServiceProviderHelper.ServiceProvider!.GetService<IServerDrivenUIService>();
_ = Task.Run(service!.FetchAsync);

return false;
Expand Down
59 changes: 54 additions & 5 deletions Maui.ServerDrivenUI/Models/CustomNamespace.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
namespace Maui.ServerDrivenUI;
using System.Text.Json.Serialization;

namespace Maui.ServerDrivenUI;

/// <summary>
/// Represents a xaml namespace eg.: xmlns:alias="clr-namespace:namespace;assembly=assembly"
/// </summary>
/// <param name="alias">Define the alias from custom namespaces</param>

Check warning on line 8 in Maui.ServerDrivenUI/Models/CustomNamespace.cs

View workflow job for this annotation

GitHub Actions / Build and publish package

XML comment has a param tag for 'alias', but there is no parameter by that name
/// <param name="namespace">clr-namespace</param>

Check warning on line 9 in Maui.ServerDrivenUI/Models/CustomNamespace.cs

View workflow job for this annotation

GitHub Actions / Build and publish package

XML comment has a param tag for 'namespace', but there is no parameter by that name
/// <param name="assembly">Assembly where the custom control is located</param>
public class CustomNamespace(string alias, string @namespace, string? assembly = null)
public class CustomNamespace : IEquatable<CustomNamespace>
{
public string Alias { get; private set; } = alias;
public string Namespace { get; private set; } = @namespace;
public string? Assembly { get; private set; } = assembly;
public string Alias
{
get; set;
}

public string Namespace
{
get; set;
}

public string? Assembly
{
get; set;
}

[JsonConstructor]
[Obsolete("This constructor should only be used from serializer")]
public CustomNamespace()
{
Alias = string.Empty;
Namespace = string.Empty;
}

public CustomNamespace(string alias, string @namespace, string? assembly = null)
{
Alias = alias;
Namespace = @namespace;
Assembly = assembly;
}

public bool Equals(CustomNamespace? other)
{
if (other is null)
return false;

return Namespace == other?.Namespace
&& Alias == other?.Alias
&& Assembly == other?.Assembly;
}

public override bool Equals(object? obj)
{
if(obj is CustomNamespace cn)
return Equals(cn);

return false;
}

public override int GetHashCode() =>
(Namespace, Alias, Assembly).GetHashCode();
}
4 changes: 3 additions & 1 deletion Maui.ServerDrivenUI/Models/ServerDrivenUISettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

using Maui.ServerDrivenUI.Services;

namespace Maui.ServerDrivenUI.Models;

internal sealed class ServerDrivenUISettings : IServerDrivenUISettings
Expand All @@ -17,6 +19,6 @@ public void AddServerElement(string key)
throw new DependencyRegistrationException($"The key: '{key}' already has been registered");
}

public void RegisterElementGetter(Func<string, Task<ServerUIElement>> uiElementGetter) =>
public void RegisterElementGetter(Func<string, IServiceProvider, Task<ServerUIElement>> uiElementGetter) =>
ElementResolver = new UIElementResolver(uiElementGetter);
}
46 changes: 38 additions & 8 deletions Maui.ServerDrivenUI/Models/ServerUIElement.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Maui.ServerDrivenUI.Services;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace Maui.ServerDrivenUI;
Expand All @@ -13,33 +14,51 @@ public class ServerUIElement
/// <summary>
/// Unique key to map your Maui Visual Element to the server json
/// </summary>
public string Key { get; set; }
public string Key
{
get; set;
}

/// <summary>
/// Maui element type
/// </summary>
public string Type { get; set; }
public string Type
{
get; set;
}

/// <summary>
/// Class with namespace eg.: "MyNamespace.Folder.ClassName"
/// </summary>
public string Class { get; set; }
public string Class
{
get; set;
}

/// <summary>
/// Visual element properties
/// </summary>
public Dictionary<string, string> Properties { get; set; }
public Dictionary<string, JsonNode> Properties
{
get; set;
}
= [];

/// <summary>
/// Custom namespaces to be used as root element
/// </summary>
public IList<CustomNamespace> CustomNamespaces { get; set; }
public IList<CustomNamespace> CustomNamespaces
{
get; set;
}

/// <summary>
/// Inner visual element content
/// </summary>
public IList<ServerUIElement> Content { get; set; }
public IList<ServerUIElement> Content
{
get; set;
}

#endregion

Expand Down Expand Up @@ -82,8 +101,19 @@ public ServerUIElement()

#region Public Methods

public string ToXaml() =>
XamlConverterService.ConvertToXml(this);
public string ToXaml(IList<CustomNamespace>? customNamespaces = null)
{
if (customNamespaces != null)
{
foreach (var customNamespace in customNamespaces)
{
if (!CustomNamespaces.Contains(customNamespace))
CustomNamespaces.Add(customNamespace);
}
}

return XamlConverterService.ConvertToXml(this);
}

#endregion
}
10 changes: 6 additions & 4 deletions Maui.ServerDrivenUI/Models/UIElementResolver.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
namespace Maui.ServerDrivenUI.Models;
using Maui.ServerDrivenUI.Services;

internal sealed class UIElementResolver(Func<string, Task<ServerUIElement>> uiElementGetter) : IUIElementResolver
namespace Maui.ServerDrivenUI.Models;

internal sealed class UIElementResolver(Func<string, IServiceProvider, Task<ServerUIElement>> uiElementGetter) : IUIElementResolver
{
private readonly Func<string, Task<ServerUIElement>> _uiElementGetter = uiElementGetter;
private readonly Func<string, IServiceProvider, Task<ServerUIElement>> _uiElementGetter = uiElementGetter;

public Task<ServerUIElement> GetElementAsync(string elementKey) =>
_uiElementGetter(elementKey);
_uiElementGetter(elementKey, ServiceProviderHelper.ServiceProvider!);
}
27 changes: 18 additions & 9 deletions Maui.ServerDrivenUI/Services/ServerDrivenUIService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class ServerDrivenUIService

private readonly TaskCompletionSource<bool> _fetchFinished = new();

private ServerUIElement[] _memoryCache = [];

#endregion

#region IServerDrivenUIService
Expand All @@ -21,11 +23,11 @@ public Task ClearCacheAsync() =>

public async Task FetchAsync()
{
var elements = await GetElementsAsync().ConfigureAwait(false);
_memoryCache = await GetElementsAsync().ConfigureAwait(false);

try
{
await SaveIntoCacheAsync(elements).ConfigureAwait(false);
await SaveIntoCacheAsync(_memoryCache).ConfigureAwait(false);
}
catch (Exception ex)
{
Expand All @@ -39,18 +41,21 @@ public async Task FetchAsync()
public async Task<string> GetXamlAsync(string elementKey)
{
// Try to get the value from cache
var element = await _cacheProvider.GetAsync<ServerUIElement>(elementKey)
var xaml = await _cacheProvider.GetAsync<string>(elementKey)
.ConfigureAwait(false);

// If the value is not in cache wait fetch finish, then try to get it from the cache again
if (!(element?.HasValue ?? true) && await _fetchFinished.Task.ConfigureAwait(false))
if (!(xaml?.HasValue ?? false) && await _fetchFinished.Task.ConfigureAwait(false))
{
element = await _cacheProvider.GetAsync<ServerUIElement>(elementKey).ConfigureAwait(false)
xaml = await _cacheProvider.GetAsync<string>(elementKey).ConfigureAwait(false)
?? throw new KeyNotFoundException($"Visual element not found for specified key: '{elementKey}'");

}

return element?.Value?.ToXaml()
var xamlValue = xaml?.Value;
if (!(xaml?.HasValue ?? false))
xamlValue = _memoryCache.FirstOrDefault(e => e.Key == elementKey)?.ToXaml();

return xamlValue
?? string.Empty;
}

Expand All @@ -59,14 +64,18 @@ public async Task<string> GetXamlAsync(string elementKey)
#region Private Methods

private Task SaveIntoCacheAsync(ServerUIElement[] elements) =>
Task.WhenAll(elements.Select(e => _cacheProvider.SetAsync(e.Key, e, _settings.UIElementCacheExpiration)));
Task.WhenAll(elements.Select(e => _cacheProvider.SetAsync(e.Key, e.ToXaml(), _settings.UIElementCacheExpiration)));

private Task<ServerUIElement[]> DownloadServerElementsAsync()
{
if (_settings.ElementResolver is null)
throw new DependencyRegistrationException("You need to set 'ElementGetter' in 'ConfigureServerDrivenUI(s=> s.RegisterElementGetter((k)=> yourApiCall(k)))' registration.");

return Task.WhenAll(_settings.CacheEntryKeys.Select(_settings.ElementResolver.GetElementAsync));
return Task.WhenAll(_settings.CacheEntryKeys.Select(async k => {
var element = await _settings.ElementResolver.GetElementAsync(k).ConfigureAwait(false);
element.Key = k;
return element;
}));
}

private async Task<ServerUIElement[]> GetElementsAsync()
Expand Down
Loading

0 comments on commit e22a86e

Please sign in to comment.