diff --git a/9.0/Apps/DeveloperBalance/AppShell.xaml b/9.0/Apps/DeveloperBalance/AppShell.xaml
index 31d32a47a..21a61ca02 100644
--- a/9.0/Apps/DeveloperBalance/AppShell.xaml
+++ b/9.0/Apps/DeveloperBalance/AppShell.xaml
@@ -33,8 +33,8 @@
SegmentWidth="40" SegmentHeight="40">
-
-
+
+
diff --git a/9.0/Apps/DeveloperBalance/AppShell.xaml.cs b/9.0/Apps/DeveloperBalance/AppShell.xaml.cs
index b21d99eda..67235f79e 100644
--- a/9.0/Apps/DeveloperBalance/AppShell.xaml.cs
+++ b/9.0/Apps/DeveloperBalance/AppShell.xaml.cs
@@ -10,6 +10,9 @@ public AppShell()
InitializeComponent();
var currentTheme = Application.Current!.RequestedTheme;
ThemeSegmentedControl.SelectedIndex = currentTheme == AppTheme.Light ? 0 : 1;
+#if ANDROID || WINDOWS
+ SemanticProperties.SetDescription(ThemeSegmentedControl, "Theme selection");
+#endif
}
public static async Task DisplaySnackbarAsync(string message)
{
diff --git a/9.0/Apps/DeveloperBalance/Data/TagRepository.cs b/9.0/Apps/DeveloperBalance/Data/TagRepository.cs
index 1e5a92ef4..c97d2b3f4 100644
--- a/9.0/Apps/DeveloperBalance/Data/TagRepository.cs
+++ b/9.0/Apps/DeveloperBalance/Data/TagRepository.cs
@@ -200,6 +200,12 @@ public async Task SaveItemAsync(Tag item, int projectID)
await Init();
await SaveItemAsync(item);
+ var isAssociated = await IsAssociated(item, projectID);
+ if (isAssociated)
+ {
+ return 0; // No need to save again if already associated
+ }
+
await using var connection = new SqliteConnection(Constants.DatabasePath);
await connection.OpenAsync();
@@ -212,6 +218,33 @@ public async Task SaveItemAsync(Tag item, int projectID)
return await saveCmd.ExecuteNonQueryAsync();
}
+ ///
+ /// Checks if a tag is already associated with a specific project.
+ ///
+ /// The tag to save.
+ /// The ID of the project.
+ /// If tag is already associated with this project
+ async Task IsAssociated(Tag item, int projectID)
+ {
+ await Init();
+ await SaveItemAsync(item);
+
+ await using var connection = new SqliteConnection(Constants.DatabasePath);
+ await connection.OpenAsync();
+
+ // First check if the association already exists
+ var checkCmd = connection.CreateCommand();
+ checkCmd.CommandText = @"
+ SELECT COUNT(*) FROM ProjectsTags
+ WHERE ProjectID = @projectID AND TagID = @tagID";
+ checkCmd.Parameters.AddWithValue("@projectID", projectID);
+ checkCmd.Parameters.AddWithValue("@tagID", item.ID);
+
+ int existingCount = Convert.ToInt32(await checkCmd.ExecuteScalarAsync());
+
+ return existingCount != 0;
+ }
+
///
/// Deletes a tag from the database.
///
diff --git a/9.0/Apps/DeveloperBalance/DeveloperBalance.csproj b/9.0/Apps/DeveloperBalance/DeveloperBalance.csproj
index 464592409..1b615f42e 100644
--- a/9.0/Apps/DeveloperBalance/DeveloperBalance.csproj
+++ b/9.0/Apps/DeveloperBalance/DeveloperBalance.csproj
@@ -1,4 +1,4 @@
-
+
net9.0-android;net9.0-ios;net9.0-maccatalyst
@@ -21,13 +21,14 @@
enable
XC0103
+ 9.0.60
true
DeveloperBalance
- com.companyname.developerbalance
+ com.companyname.developerbalance
1.0
@@ -63,7 +64,7 @@
-
+
@@ -72,7 +73,7 @@
-
+
diff --git a/9.0/Apps/DeveloperBalance/MauiProgram.cs b/9.0/Apps/DeveloperBalance/MauiProgram.cs
index 901929564..50dabb401 100644
--- a/9.0/Apps/DeveloperBalance/MauiProgram.cs
+++ b/9.0/Apps/DeveloperBalance/MauiProgram.cs
@@ -1,5 +1,7 @@
using CommunityToolkit.Maui;
using Microsoft.Extensions.Logging;
+using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Platform;
using Syncfusion.Maui.Toolkit.Hosting;
namespace DeveloperBalance;
@@ -16,9 +18,23 @@ public static MauiApp CreateMauiApp()
.ConfigureMauiHandlers(handlers =>
{
#if IOS || MACCATALYST
- handlers.AddHandler();
+ handlers.AddHandler();
#endif
- })
+#if WINDOWS
+ Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler.Mapper.AppendToMapping("KeyboardAccessibleCollectionView", (handler, view) =>
+ {
+ handler.PlatformView.SingleSelectionFollowsFocus = false;
+ });
+
+ Microsoft.Maui.Handlers.ContentViewHandler.Mapper.AppendToMapping(nameof(Pages.Controls.CategoryChart), (handler, view) =>
+ {
+ if (view is Pages.Controls.CategoryChart && handler.PlatformView is ContentPanel contentPanel)
+ {
+ contentPanel.IsTabStop = true;
+ }
+ });
+#endif
+ })
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
diff --git a/9.0/Apps/DeveloperBalance/PageModels/MainPageModel.cs b/9.0/Apps/DeveloperBalance/PageModels/MainPageModel.cs
index 986840a50..462807802 100644
--- a/9.0/Apps/DeveloperBalance/PageModels/MainPageModel.cs
+++ b/9.0/Apps/DeveloperBalance/PageModels/MainPageModel.cs
@@ -35,6 +35,9 @@ public partial class MainPageModel : ObservableObject, IProjectTaskPageModel
[ObservableProperty]
private string _today = DateTime.Now.ToString("dddd, MMM d");
+ [ObservableProperty]
+ private Project? selectedProject;
+
public bool HasCompletedTasks
=> Tasks?.Any(t => t.IsCompleted) ?? false;
@@ -149,8 +152,8 @@ private Task AddTask()
=> Shell.Current.GoToAsync($"task");
[RelayCommand]
- private Task NavigateToProject(Project project)
- => Shell.Current.GoToAsync($"project?id={project.ID}");
+ private Task? NavigateToProject(Project project)
+ => project is null ? null : Shell.Current.GoToAsync($"project?id={project.ID}");
[RelayCommand]
private Task NavigateToTask(ProjectTask task)
diff --git a/9.0/Apps/DeveloperBalance/PageModels/ProjectDetailPageModel.cs b/9.0/Apps/DeveloperBalance/PageModels/ProjectDetailPageModel.cs
index a2399d031..da795f956 100644
--- a/9.0/Apps/DeveloperBalance/PageModels/ProjectDetailPageModel.cs
+++ b/9.0/Apps/DeveloperBalance/PageModels/ProjectDetailPageModel.cs
@@ -1,6 +1,8 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using DeveloperBalance.Models;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
namespace DeveloperBalance.PageModels;
@@ -34,6 +36,8 @@ public partial class ProjectDetailPageModel : ObservableObject, IQueryAttributab
[ObservableProperty]
private List _allTags = [];
+ public IList