Skip to content

Commit

Permalink
Fixes + demos
Browse files Browse the repository at this point in the history
  • Loading branch information
felipebaltazar committed Feb 18, 2024
1 parent 3244016 commit d6b9629
Show file tree
Hide file tree
Showing 14 changed files with 530 additions and 56 deletions.
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 with dotnet

XML comment has a param tag for 'alias', but there is no parameter by that name

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

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

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 with dotnet

XML comment has a param tag for 'namespace', but there is no parameter by that name

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

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

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 d6b9629

Please sign in to comment.