diff --git a/README.md b/README.md
index 9747c9af..d1ed912a 100644
--- a/README.md
+++ b/README.md
@@ -4,12 +4,6 @@
C'est une application mettant en oeuvre une prise de contact entre un demandeur de services et son éventuel prestataire associé.
-# Statut actuel des actions Forgejo
-
-
-
-
-
# Statut actuel des actions GitHub
* [](https://github.com/pazof/yavsc/actions/workflows/docker-publish-android.yml)
diff --git a/src/PostIt/Directory.Packages.props b/src/PostIt/Directory.Packages.props
index 62b3a343..900f1428 100644
--- a/src/PostIt/Directory.Packages.props
+++ b/src/PostIt/Directory.Packages.props
@@ -14,7 +14,6 @@
-
diff --git a/src/PostIt/PostIt/App.axaml.cs b/src/PostIt/PostIt/App.axaml.cs
index 6f93edf9..e59e0d33 100644
--- a/src/PostIt/PostIt/App.axaml.cs
+++ b/src/PostIt/PostIt/App.axaml.cs
@@ -59,9 +59,6 @@ public partial class App : Application
var client = new BlogApiClient(api, settings.BlogsApiUrl);
var circleClient = new CircleApiClient(api, settings.BlogsApiUrl);
var blogAclClient = new BlogAclApiClient(api, settings.BlogsApiUrl);
- var userSearchClient = new UserSearchClient(api, settings.BlogsApiUrl);
- var contactService = new ContactService();
- var userDirectory = new UserDirectory(userSearchClient);
var services = new ServiceCollection();
@@ -90,9 +87,6 @@ public partial class App : Application
services.AddSingleton(client);
services.AddSingleton(circleClient);
services.AddSingleton(blogAclClient);
- services.AddSingleton(userSearchClient);
- services.AddSingleton(contactService);
- services.AddSingleton(userDirectory);
services.AddTransient();
services.AddTransient();
services.AddTransient();
diff --git a/src/PostIt/PostIt/Services/ContactService.Desktop.cs b/src/PostIt/PostIt/Services/ContactService.Desktop.cs
deleted file mode 100644
index fa7d37f6..00000000
--- a/src/PostIt/PostIt/Services/ContactService.Desktop.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-#if !ANDROID && !IOS
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace PostIt.Services;
-
-///
-/// Desktop stub for .
-///
-/// The desktop has no equivalent of the mobile address
-/// book (no Contacts.Default, no CardDAV out of the
-/// box). Rather than synthesise a list from a different
-/// source, this provider returns an empty list and lets the
-/// UI render an honest "no local contacts on this platform"
-/// message.
-///
-/// If desktop users want to invite people who aren't
-/// Yavsc members, that flow goes through a separate path
-/// (manual email entry + invitation endpoint) — not through
-/// . Finding existing Yavsc
-/// members is 's job, not this
-/// one's.
-///
-/// Future CardDAV / Google Contacts / Exchange
-/// providers can plug in here as additional
-/// implementations selected
-/// from DI by configuration.
-///
-public sealed class ContactService : IContactService
-{
- public Task> GetDeviceContactsAsync(CancellationToken ct = default)
- => Task.FromResult>(Array.Empty());
-}
-#endif
diff --git a/src/PostIt/PostIt/Services/ContactService.Mobile.cs b/src/PostIt/PostIt/Services/ContactService.Mobile.cs
deleted file mode 100644
index 8dbd134d..00000000
--- a/src/PostIt/PostIt/Services/ContactService.Mobile.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-#if ANDROID || IOS
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Maui.ApplicationModel.Communication;
-using Microsoft.Maui.ApplicationModel;
-using Microsoft.Maui.Devices;
-
-namespace PostIt.Services;
-
-///
-/// Mobile implementation backed by MAUI Essentials
-/// Contacts.Default.
-///
-/// Compiled only for ANDROID and IOS. On desktop targets,
-/// see ContactService.Desktop.cs (the stub that wins at
-/// compile time).
-///
-/// Note: at runtime, this class throws
-/// NotImplementedInReferenceAssemblyException unless
-/// the host application project also references the
-/// platform-specific Microsoft.Maui.Essentials implementation
-/// (typically PostIt.Android). On iOS the same is
-/// required via PostIt.iOS. On desktop the stub is used
-/// and this file is excluded.
-///
-public sealed class ContactService : IContactService
-{
- public async Task> GetDeviceContactsAsync(CancellationToken ct = default)
- {
- if (DeviceInfo.Current.Platform == DevicePlatform.Unknown)
- return Array.Empty();
-
- try
- {
- var status = await Permissions.RequestAsync();
- if (status != PermissionStatus.Granted)
- return Array.Empty();
-
- var contacts = await Contacts.Default.GetAllAsync();
- if (contacts is null) return Array.Empty();
-
- // Carry the per-contact email list as-is. A real
- // device contact can carry several addresses (home /
- // work / other); the UI use case ("invite / add to a
- // circle") can then decide which address to use, or
- // let the user pick. The platform-neutral ContactDto
- // shape is intentionally richer than the Yavsc
- // directory's single-Email shape — the two flows
- // answer different questions.
- var result = new List(contacts.Count);
- foreach (var c in contacts)
- {
- var emails = ExtractEmails(c.Emails);
- result.Add(new ContactDto(
- c.Id,
- c.DisplayName ?? string.Empty,
- emails));
- }
- return result;
- }
- catch (Exception ex)
- {
- System.Diagnostics.Debug.WriteLine($"ContactService: {ex.Message}");
- return Array.Empty();
- }
- }
-
- private static IReadOnlyList ExtractEmails(IEnumerable? emails)
- {
- if (emails is null) return Array.Empty();
- var list = new List();
- foreach (var e in emails)
- {
- if (!string.IsNullOrEmpty(e.EmailAddress))
- list.Add(e.EmailAddress);
- }
- return list;
- }
-}
-#endif
diff --git a/src/PostIt/PostIt/Services/IContactService.cs b/src/PostIt/PostIt/Services/IContactService.cs
deleted file mode 100644
index 49ca3064..00000000
--- a/src/PostIt/PostIt/Services/IContactService.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace PostIt.Services;
-
-///
-/// Abstraction over the device-local address book. Used by
-/// the "invite someone" flow to enumerate people the user
-/// already has in their phone — including people who have
-/// never heard of Yavsc.
-///
-/// Distinct from , which
-/// reads the central Yavsc user table. A device contact may
-/// not have a Yavsc account; a directory entry always does.
-/// The two are exposed as separate interfaces so a UI that
-/// needs both can take both by constructor injection and
-/// present them under separate sections (e.g. "Contacts from
-/// your phone" vs "Yavsc members").
-///
-/// Implementations live next to this file in
-/// platform-conditional source files:
-/// ContactService.Mobile.cs (ANDROID/IOS) and
-/// ContactService.Desktop.cs (everything else). On
-/// desktop the implementation is a stub that returns an
-/// empty list: the desktop has no equivalent of the mobile
-/// address book, and inviting from a desktop is a separate
-/// flow.
-///
-public interface IContactService
-{
- ///
- /// Read the device address book. Returns the contacts
- /// known to the local provider; on desktop (no local
- /// provider) this is always an empty list.
- ///
- Task> GetDeviceContactsAsync(CancellationToken ct = default);
-}
-
-///
-/// Platform-neutral contact DTO. Source-of-truth shape for
-/// the UI layer; concrete providers (MAUI Essentials on
-/// mobile) map to this type.
-///
-/// Emails is a list on purpose: a real device
-/// contact may carry several addresses (home / work / other).
-/// The UI use case ("invite / add to a circle") can then
-/// decide which address to use, or let the user pick. This
-/// is intentionally richer than the Yavsc directory's
-/// single-Email shape — the two flows answer different
-/// questions and shouldn't be flattened onto the same
-/// wire.
-///
-public sealed record ContactDto(
- string Id,
- string DisplayName,
- IReadOnlyList Emails);
diff --git a/src/PostIt/PostIt/Services/IUserDirectory.cs b/src/PostIt/PostIt/Services/IUserDirectory.cs
deleted file mode 100644
index 7d4c1eb4..00000000
--- a/src/PostIt/PostIt/Services/IUserDirectory.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace PostIt.Services;
-
-///
-/// Abstraction over the central Yavsc user directory. Used by
-/// the "add to a circle" flow to find Yavsc users by display
-/// name or email.
-///
-/// Distinct from , which
-/// reads the device-local address book. A Yavsc user
-/// directory entry is always a registered account; a device
-/// contact may be anyone in the user's phone — including
-/// people who have never heard of Yavsc.
-///
-/// Implementations live next to this file in
-/// platform-conditional source files:
-/// UserDirectory.Desktop.cs and
-/// UserDirectory.Mobile.cs. Both currently delegate to
-/// UserSearchClient (the central /api/user-search
-/// endpoint); the split exists so future platform-specific
-/// sources (offline cache, directory-scoped providers) can be
-/// plugged in without disturbing the consumer.
-///
-public interface IUserDirectory
-{
- ///
- /// Search the directory by display name (substring) and/or
- /// email (exact).
- ///
- /// Substring filter on the user's
- /// display name. Empty or whitespace short-circuits to an
- /// empty list (matches the client UX of "type to search",
- /// not "show me a directory").
- /// Cancellation token.
- /// A flat list of matching directory entries.
- /// Never null; may be empty.
- Task> SearchAsync(string query, CancellationToken ct = default);
-}
-
-///
-/// Platform-neutral summary of a Yavsc directory entry. Mirrors
-/// the wire shape of /api/user-search (see
-/// UserSearchResultDto) but expressed in terms that
-/// don't leak transport concerns.
-///
-/// Kept as a record on purpose: directory entries are
-/// immutable snapshots from the server, so structural equality
-/// makes "did the user already pick this one?" trivial.
-///
-public sealed record UserSummary(
- string Id,
- string UserName,
- string? FullName,
- string? Avatar,
- string? Email)
-{
- ///
- /// Convenience for "what to show in a picker". Falls back
- /// to when
- /// is null or empty.
- ///
- public string DisplayName =>
- string.IsNullOrWhiteSpace(FullName) ? UserName : FullName;
-}
diff --git a/src/PostIt/PostIt/Services/UserDirectory.Desktop.cs b/src/PostIt/PostIt/Services/UserDirectory.Desktop.cs
deleted file mode 100644
index c821bb87..00000000
--- a/src/PostIt/PostIt/Services/UserDirectory.Desktop.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-#if !ANDROID && !IOS
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Yavsc.Api.Client;
-
-namespace PostIt.Services;
-
-///
-/// Desktop implementation of .
-/// Delegates to the central /api/user-search endpoint
-/// via .
-///
-/// The desktop has no device-local address book, so the
-/// "add to a circle" flow on desktop is Yavsc-users-only.
-/// Inviting someone who doesn't have a Yavsc account from
-/// desktop is a separate feature (manual email entry +
-/// invitation endpoint) and lives outside this interface.
-///
-public sealed class UserDirectory : IUserDirectory
-{
- private readonly UserSearchClient _client;
-
- public UserDirectory(UserSearchClient client)
- {
- _client = client ?? throw new ArgumentNullException(nameof(client));
- }
-
- public async Task> SearchAsync(
- string query, CancellationToken ct = default)
- {
- // UserSearchClient already short-circuits on empty
- // queries, but do it here too so the contract is
- // obvious to anyone reading IUserDirectory alone
- // without having to chase the client wrapper.
- if (string.IsNullOrWhiteSpace(query))
- return Array.Empty();
-
- var results = await _client.SearchAsync(query: query, ct: ct).ConfigureAwait(false);
- if (results is null) return Array.Empty();
-
- return results.Select(u => new UserSummary(
- Id: u.Id,
- UserName: u.UserName,
- FullName: u.FullName,
- Avatar: u.Avatar,
- Email: u.Email)).ToList();
- }
-}
-#endif
diff --git a/src/PostIt/PostIt/Services/UserDirectory.Mobile.cs b/src/PostIt/PostIt/Services/UserDirectory.Mobile.cs
deleted file mode 100644
index 5cba6e4a..00000000
--- a/src/PostIt/PostIt/Services/UserDirectory.Mobile.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-#if ANDROID || IOS
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Yavsc.Api.Client;
-
-namespace PostIt.Services;
-
-///
-/// Mobile implementation of .
-/// Same backing as the desktop provider (the central
-/// /api/user-search endpoint via
-/// ) — mobile devices have the
-/// network too, and "add to a circle" needs the same directory
-/// regardless of platform.
-///
-/// The split exists so a future mobile-only provider
-/// (offline cache, device-local mirror of the user's own
-/// circles) can be plugged in without touching consumers.
-///
-public sealed class UserDirectory : IUserDirectory
-{
- private readonly UserSearchClient _client;
-
- public UserDirectory(UserSearchClient client)
- {
- _client = client ?? throw new ArgumentNullException(nameof(client));
- }
-
- public async Task> SearchAsync(
- string query, CancellationToken ct = default)
- {
- if (string.IsNullOrWhiteSpace(query))
- return Array.Empty();
-
- var results = await _client.SearchAsync(query: query, ct: ct).ConfigureAwait(false);
- if (results is null) return Array.Empty();
-
- return results.Select(u => new UserSummary(
- Id: u.Id,
- UserName: u.UserName,
- FullName: u.FullName,
- Avatar: u.Avatar,
- Email: u.Email)).ToList();
- }
-}
-#endif
diff --git a/src/Yavsc.Api.Client/Dtos/UserSearchResultDto.cs b/src/Yavsc.Api.Client/Dtos/UserSearchResultDto.cs
deleted file mode 100644
index d77f50e7..00000000
--- a/src/Yavsc.Api.Client/Dtos/UserSearchResultDto.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Yavsc.Api.Client.Dtos;
-
-///
-/// Wire format for GET /api/user-search.
-///
-/// Mirrors the server-side
-/// Yavsc.Blogs.Controllers.UserSearchResultDto but stops
-/// short of any entity navigation properties. Only the fields
-/// a client address book needs (id, name, avatar, email) are
-/// included.
-///
-/// Field names match the JSON the server emits (camelCase
-/// via the default policy), so
-/// no [JsonPropertyName] attributes are required.
-///
-public sealed class UserSearchResultDto
-{
- public string Id { get; set; } = string.Empty;
- public string UserName { get; set; } = string.Empty;
- public string? FullName { get; set; }
- public string? Avatar { get; set; }
- public string? Email { get; set; }
-}
\ No newline at end of file
diff --git a/src/Yavsc.Api.Client/UserSearchClient.cs b/src/Yavsc.Api.Client/UserSearchClient.cs
deleted file mode 100644
index d1aef6be..00000000
--- a/src/Yavsc.Api.Client/UserSearchClient.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Yavsc.Api.Client.Dtos;
-
-namespace Yavsc.Api.Client;
-
-///
-/// HTTP client for /api/user-search on the Yavsc Blogs
-/// server. Used by client-side address books (PostIt.Desktop,
-/// future PostIt.Browser CLI, …) to look up Yavsc users by
-/// display name or email.
-///
-/// The server scopes every endpoint to the authenticated
-/// caller; any authenticated user can search the user table of
-/// the instance. There is no per-user filtering on the response
-/// side — this is by design on single-tenant deployments
-/// (closed community). Multi-tenant deployments should gate
-/// this controller behind a tenant-scoped policy before
-/// exposing it; see the server-side
-/// UserSearchApiController doc for details.
-///
-public sealed class UserSearchClient
-{
- private const string Path = "user-search";
-
- private readonly IYavscApiClient _api;
-
- public UserSearchClient(IYavscApiClient api, string blogsBaseAddress)
- {
- _api = api ?? throw new ArgumentNullException(nameof(api));
- if (string.IsNullOrEmpty(blogsBaseAddress))
- throw new ArgumentException("Base address is required.", nameof(blogsBaseAddress));
-
- if (api.Http.BaseAddress is null)
- api.Http.BaseAddress = new Uri(blogsBaseAddress);
- }
-
- ///
- /// Search users by display name (substring) or email (exact).
- ///
- /// Substring filter on FullName or
- /// UserName. Empty or null returns an empty list (the server
- /// would return all users, which we don't want by
- /// default).
- /// Optional exact-match filter on
- /// Email.
- /// Maximum results, capped at 100.
- /// Default 25.
- public Task> SearchAsync(
- string? query = null,
- string? email = null,
- int take = 25,
- CancellationToken ct = default)
- {
- // Match the server's contract: at least one filter is
- // expected. The server doesn't enforce this (an empty
- // query + empty email returns the first `take` users
- // alphabetically), but the address-book UX is "type
- // something to search", so we short-circuit empty
- // queries client-side.
- if (string.IsNullOrWhiteSpace(query) && string.IsNullOrWhiteSpace(email))
- return Task.FromResult(new List());
-
- var qs = new List();
- if (!string.IsNullOrWhiteSpace(query))
- qs.Add($"q={Uri.EscapeDataString(query)}");
- if (!string.IsNullOrWhiteSpace(email))
- qs.Add($"e={Uri.EscapeDataString(email)}");
- qs.Add($"take={Math.Clamp(take, 1, 100)}");
-
- return _api.CallAsync>(
- HttpMethod.Get,
- $"{Path}?{string.Join('&', qs)}",
- ct: ct);
- }
-}
\ No newline at end of file