diff --git a/docs/content/samples/todoapp/maui.md b/docs/content/samples/todoapp/maui.md index c21bfcf5..a2445809 100644 --- a/docs/content/samples/todoapp/maui.md +++ b/docs/content/samples/todoapp/maui.md @@ -22,7 +22,13 @@ Before you begin adjusting the application for offline usage, you must [deploy a ## Update the application for datasync operations -All the changes are isolated to the `Database/AppDbContext.cs` file. +All the changes are isolated to the `Models/AppDbContext.cs` file. You can change the definition of `OFFLINE_SYNC_ENABLED` at the top of the file to make all the changes. + +```csharp +#define OFFLINE_SYNC_ENABLED +``` + +The following changes are made to the database context: 1. Change the definition of the class so that it inherits from `OfflineDbContext`: diff --git a/samples/todoapp/TodoApp.MAUI/MainPage.xaml b/samples/todoapp/TodoApp.MAUI/MainPage.xaml index 1ba382a7..53338732 100644 --- a/samples/todoapp/TodoApp.MAUI/MainPage.xaml +++ b/samples/todoapp/TodoApp.MAUI/MainPage.xaml @@ -1,19 +1,23 @@ - + - + - + @@ -22,8 +26,8 @@ - - + + @@ -37,23 +41,26 @@ - + - + ReturnCommandParameter="{Binding Text, Source={x:Reference addItemEntry}}" + Style="{StaticResource addItemEntry}" /> diff --git a/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs b/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs index 0701f1f4..33650c8d 100644 --- a/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs +++ b/samples/todoapp/TodoApp.MAUI/MainPage.xaml.cs @@ -21,14 +21,14 @@ public MainPage() protected override void OnAppearing() { base.OnAppearing(); - this._viewModel.RefreshItemsCommand.Execute(); + this._viewModel.RefreshItemsCommand.Execute(null); } public void OnListItemTapped(object sender, ItemTappedEventArgs e) { if (e.Item is TodoItem item) { - this._viewModel.UpdateItemCommand.Execute(item); + this._viewModel.UpdateItemCommand.Execute(item.Id); } if (sender is ListView itemList) diff --git a/samples/todoapp/TodoApp.MAUI/Models/AppDbContext.cs b/samples/todoapp/TodoApp.MAUI/Models/AppDbContext.cs index 5081504a..5ce6cb57 100644 --- a/samples/todoapp/TodoApp.MAUI/Models/AppDbContext.cs +++ b/samples/todoapp/TodoApp.MAUI/Models/AppDbContext.cs @@ -2,37 +2,50 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#undef OFFLINE_SYNC_ENABLED + +using CommunityToolkit.Datasync.Client.Http; +using CommunityToolkit.Datasync.Client.Offline; using Microsoft.EntityFrameworkCore; +using TodoApp.MAUI.Services; namespace TodoApp.MAUI.Models; +#if OFFLINE_SYNC_ENABLED +public class AppDbContext(DbContextOptions options) : OfflineDbContext(options) +#else public class AppDbContext(DbContextOptions options) : DbContext(options) +#endif { public DbSet TodoItems => Set(); - //protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) - //{ - // HttpClientOptions clientOptions = new() - // { - // Endpoint = new Uri("https://YOURSITEHERE.azurewebsites.net/"), - // HttpPipeline = [new LoggingHandler()] - // }; - // _ = optionsBuilder.UseHttpClientOptions(clientOptions); - //} +#if OFFLINE_SYNC_ENABLED + protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) + { + HttpClientOptions clientOptions = new() + { + Endpoint = new Uri("https://YOUR_SITE_HERE.azurewebsites.net/"), + HttpPipeline = [new LoggingHandler()] + }; + _ = optionsBuilder.UseHttpClientOptions(clientOptions); + } +#endif public async Task SynchronizeAsync(CancellationToken cancellationToken = default) { - //PushResult pushResult = await this.PushAsync(cancellationToken); - //if (!pushResult.IsSuccessful) - //{ - // throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); - //} - - //PullResult pullResult = await this.PullAsync(cancellationToken); - //if (!pullResult.IsSuccessful) - //{ - // throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); - //} +#if OFFLINE_SYNC_ENABLED + PushResult pushResult = await this.PushAsync(cancellationToken); + if (!pushResult.IsSuccessful) + { + throw new ApplicationException($"Push failed: {pushResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); + } + + PullResult pullResult = await this.PullAsync(cancellationToken); + if (!pullResult.IsSuccessful) + { + throw new ApplicationException($"Pull failed: {pullResult.FailedRequests.FirstOrDefault().Value.ReasonPhrase}"); + } +#endif } } diff --git a/samples/todoapp/TodoApp.MAUI/Services/LoggingHandler.cs b/samples/todoapp/TodoApp.MAUI/Services/LoggingHandler.cs new file mode 100644 index 00000000..0d869c11 --- /dev/null +++ b/samples/todoapp/TodoApp.MAUI/Services/LoggingHandler.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace TodoApp.MAUI.Services; + +/// +/// A delegating handler that logs the request/response to stdout. +/// +public class LoggingHandler : DelegatingHandler +{ + public LoggingHandler() : base() + { + } + + public LoggingHandler(HttpMessageHandler innerHandler) : base(innerHandler) + { + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + Debug.WriteLine($"[HTTP] >>> {request.Method} {request.RequestUri}"); + await WriteContentAsync(request.Content, cancellationToken); + + HttpResponseMessage response = await base.SendAsync(request, cancellationToken); + + Debug.WriteLine($"[HTTP] <<< {response.StatusCode} {response.ReasonPhrase}"); + await WriteContentAsync(response.Content, cancellationToken); + + return response; + } + + private static async Task WriteContentAsync(HttpContent? content, CancellationToken cancellationToken = default) + { + if (content != null) + { + Debug.WriteLine($"[HTTP] >>> {await content.ReadAsStringAsync(cancellationToken)}"); + } + } +} + diff --git a/samples/todoapp/TodoApp.MAUI/ViewModels/MainViewModel.cs b/samples/todoapp/TodoApp.MAUI/ViewModels/MainViewModel.cs index d3e2d3a0..660c51d1 100644 --- a/samples/todoapp/TodoApp.MAUI/ViewModels/MainViewModel.cs +++ b/samples/todoapp/TodoApp.MAUI/ViewModels/MainViewModel.cs @@ -4,13 +4,14 @@ using CommunityToolkit.Datasync.Client; using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; using Microsoft.EntityFrameworkCore; using TodoApp.MAUI.Models; using TodoApp.MAUI.Services; namespace TodoApp.MAUI.ViewModels; -public class MainViewModel(AppDbContext context, IAlertService alertService) : ObservableRecipient +public partial class MainViewModel(AppDbContext context, IAlertService alertService) : ObservableRecipient { [ObservableProperty] private bool _isRefreshing = false; @@ -18,6 +19,7 @@ public class MainViewModel(AppDbContext context, IAlertService alertService) : O [ObservableProperty] private ConcurrentObservableCollection items = []; + [RelayCommand] public async Task RefreshItemsAsync(CancellationToken cancellationToken = default) { if (IsRefreshing) @@ -41,6 +43,7 @@ public async Task RefreshItemsAsync(CancellationToken cancellationToken = defaul } } + [RelayCommand] public async Task UpdateItemAsync(string itemId, CancellationToken cancellationToken = default) { try @@ -60,6 +63,7 @@ public async Task UpdateItemAsync(string itemId, CancellationToken cancellationT } } + [RelayCommand] public async Task AddItemAsync(string text, CancellationToken cancellationToken = default) { try