diff --git a/BuyMeCofee.Maui/BmcConfiguration.cs b/BuyMeCofee.Maui/BmcConfiguration.cs
new file mode 100644
index 0000000..9f8e22a
--- /dev/null
+++ b/BuyMeCofee.Maui/BmcConfiguration.cs
@@ -0,0 +1,15 @@
+using BuyMeCofee.Maui.Services;
+
+namespace BuyMeCofee.Maui;
+
+///
+/// Static configuration holder for the Buy Me a Coffee library.
+/// Populated by .
+///
+public static class BmcConfiguration
+{
+ ///
+ /// The supporter lookup service. Null if no AccessToken was configured.
+ ///
+ public static BmcSupporterService? SupporterService { get; internal set; }
+}
diff --git a/BuyMeCofee.Maui/BmcOptions.cs b/BuyMeCofee.Maui/BmcOptions.cs
new file mode 100644
index 0000000..03350c2
--- /dev/null
+++ b/BuyMeCofee.Maui/BmcOptions.cs
@@ -0,0 +1,18 @@
+namespace BuyMeCofee.Maui;
+
+///
+/// Configuration options for the Buy Me a Coffee library.
+///
+public class BmcOptions
+{
+ ///
+ /// Your Buy Me a Coffee API access token (from https://developers.buymeacoffee.com).
+ /// Required for supporter verification. This is the creator's token.
+ ///
+ public string? AccessToken { get; set; }
+
+ ///
+ /// How long to cache the supporter list before re-fetching. Default: 1 hour.
+ ///
+ public TimeSpan CacheDuration { get; set; } = TimeSpan.FromHours(1);
+}
diff --git a/BuyMeCofee.Maui/BuyMeACoffeeExtensions.cs b/BuyMeCofee.Maui/BuyMeACoffeeExtensions.cs
index 5803941..81eec42 100644
--- a/BuyMeCofee.Maui/BuyMeACoffeeExtensions.cs
+++ b/BuyMeCofee.Maui/BuyMeACoffeeExtensions.cs
@@ -1,3 +1,4 @@
+using BuyMeCofee.Maui.Services;
using SkiaSharp.Views.Maui.Controls.Hosting;
namespace BuyMeCofee.Maui;
@@ -13,4 +14,32 @@ public static class BuyMeACoffeeExtensions
builder.UseSkiaSharp();
return builder;
}
+
+ ///
+ /// Registers Buy Me a Coffee controls with optional supporter verification.
+ /// When an AccessToken is provided, controls with HideIfSupporter=true
+ /// will auto-hide for verified supporters.
+ ///
+ ///
+ /// builder.UseBuyMeACoffee(options =>
+ /// {
+ /// options.AccessToken = "your-bmc-api-token";
+ /// options.CacheDuration = TimeSpan.FromHours(2);
+ /// });
+ ///
+ public static MauiAppBuilder UseBuyMeACoffee(this MauiAppBuilder builder, Action configure)
+ {
+ builder.UseSkiaSharp();
+
+ var options = new BmcOptions();
+ configure(options);
+
+ if (!string.IsNullOrWhiteSpace(options.AccessToken))
+ {
+ BmcConfiguration.SupporterService = new BmcSupporterService(
+ options.AccessToken, options.CacheDuration);
+ }
+
+ return builder;
+ }
}
diff --git a/BuyMeCofee.Maui/BuyMeCofee.Maui.csproj b/BuyMeCofee.Maui/BuyMeCofee.Maui.csproj
index 109a1b4..76267ed 100644
--- a/BuyMeCofee.Maui/BuyMeCofee.Maui.csproj
+++ b/BuyMeCofee.Maui/BuyMeCofee.Maui.csproj
@@ -31,6 +31,8 @@
git
maui;buymeacoffee;bmc;donation;tip;controls
README.md
+ 1.1.0
+ v1.1.0: Crisp SVG branding via MAUI asset pipeline, supporter verification API (auto-hide controls for existing supporters), SupporterEmail/HideIfSupporter properties on all controls.
false
15.0
@@ -47,6 +49,7 @@
+
diff --git a/BuyMeCofee.Maui/Controls/BuyMeACoffeeButton.cs b/BuyMeCofee.Maui/Controls/BuyMeACoffeeButton.cs
index 2b37c70..c25399d 100644
--- a/BuyMeCofee.Maui/Controls/BuyMeACoffeeButton.cs
+++ b/BuyMeCofee.Maui/Controls/BuyMeACoffeeButton.cs
@@ -45,6 +45,14 @@ public class BuyMeACoffeeButton : ContentView
BindableProperty.Create(nameof(CupSize), typeof(double), typeof(BuyMeACoffeeButton),
28.0, propertyChanged: OnVisualPropertyChanged);
+ public static readonly BindableProperty SupporterEmailProperty =
+ BindableProperty.Create(nameof(SupporterEmail), typeof(string), typeof(BuyMeACoffeeButton),
+ null, propertyChanged: OnSupporterPropertyChanged);
+
+ public static readonly BindableProperty HideIfSupporterProperty =
+ BindableProperty.Create(nameof(HideIfSupporter), typeof(bool), typeof(BuyMeACoffeeButton),
+ false, propertyChanged: OnSupporterPropertyChanged);
+
#endregion
#region Properties
@@ -105,6 +113,25 @@ public class BuyMeACoffeeButton : ContentView
set => SetValue(CupSizeProperty, value);
}
+ ///
+ /// Email address to check against your BMC supporters list.
+ /// Requires AccessToken configured via UseBuyMeACoffee(options => ...).
+ ///
+ public string? SupporterEmail
+ {
+ get => (string?)GetValue(SupporterEmailProperty);
+ set => SetValue(SupporterEmailProperty, value);
+ }
+
+ ///
+ /// When true, the control auto-hides if SupporterEmail is a verified supporter. Default: false.
+ ///
+ public bool HideIfSupporter
+ {
+ get => (bool)GetValue(HideIfSupporterProperty);
+ set => SetValue(HideIfSupporterProperty, value);
+ }
+
#endregion
private Border _border = null!;
@@ -230,4 +257,10 @@ public class BuyMeACoffeeButton : ContentView
if (bindable is BuyMeACoffeeButton button)
button.ApplyTheme();
}
+
+ private static void OnSupporterPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is BuyMeACoffeeButton button)
+ BmcSupporterCheck.CheckAndHide(button, button.SupporterEmail, button.HideIfSupporter);
+ }
}
diff --git a/BuyMeCofee.Maui/Controls/BuyMeACoffeeQrCode.cs b/BuyMeCofee.Maui/Controls/BuyMeACoffeeQrCode.cs
index e9a6141..34dfb62 100644
--- a/BuyMeCofee.Maui/Controls/BuyMeACoffeeQrCode.cs
+++ b/BuyMeCofee.Maui/Controls/BuyMeACoffeeQrCode.cs
@@ -35,6 +35,14 @@ public class BuyMeACoffeeQrCode : ContentView
BindableProperty.Create(nameof(LogoSizeFraction), typeof(double), typeof(BuyMeACoffeeQrCode),
0.25, propertyChanged: OnVisualPropertyChanged);
+ public static readonly BindableProperty SupporterEmailProperty =
+ BindableProperty.Create(nameof(SupporterEmail), typeof(string), typeof(BuyMeACoffeeQrCode),
+ null, propertyChanged: OnSupporterPropertyChanged);
+
+ public static readonly BindableProperty HideIfSupporterProperty =
+ BindableProperty.Create(nameof(HideIfSupporter), typeof(bool), typeof(BuyMeACoffeeQrCode),
+ false, propertyChanged: OnSupporterPropertyChanged);
+
#endregion
#region Properties
@@ -67,6 +75,25 @@ public class BuyMeACoffeeQrCode : ContentView
set => SetValue(BackgroundColorProperty, value);
}
+ ///
+ /// Email address to check against your BMC supporters list.
+ /// Requires AccessToken configured via UseBuyMeACoffee(options => ...).
+ ///
+ public string? SupporterEmail
+ {
+ get => (string?)GetValue(SupporterEmailProperty);
+ set => SetValue(SupporterEmailProperty, value);
+ }
+
+ ///
+ /// When true, the control auto-hides if SupporterEmail is a verified supporter. Default: false.
+ ///
+ public bool HideIfSupporter
+ {
+ get => (bool)GetValue(HideIfSupporterProperty);
+ set => SetValue(HideIfSupporterProperty, value);
+ }
+
/// Logo size as fraction of QR code size (0.0 - 0.35). Default: 0.25
public double LogoSizeFraction
{
@@ -99,6 +126,11 @@ public class BuyMeACoffeeQrCode : ContentView
try
{
using var stream = BmcBrandAssets.GetCupLogoStream();
+ if (stream is null)
+ {
+ _logoBitmap = null;
+ return;
+ }
_logoBitmap = SKBitmap.Decode(stream);
}
catch
@@ -215,5 +247,11 @@ public class BuyMeACoffeeQrCode : ContentView
}
}
+ private static void OnSupporterPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is BuyMeACoffeeQrCode qr)
+ BmcSupporterCheck.CheckAndHide(qr, qr.SupporterEmail, qr.HideIfSupporter);
+ }
+
#endregion
}
diff --git a/BuyMeCofee.Maui/Controls/BuyMeACoffeeWidget.cs b/BuyMeCofee.Maui/Controls/BuyMeACoffeeWidget.cs
index d9560bb..0ff42bd 100644
--- a/BuyMeCofee.Maui/Controls/BuyMeACoffeeWidget.cs
+++ b/BuyMeCofee.Maui/Controls/BuyMeACoffeeWidget.cs
@@ -41,6 +41,14 @@ public class BuyMeACoffeeWidget : ContentView
BindableProperty.Create(nameof(SupportButtonText), typeof(string), typeof(BuyMeACoffeeWidget),
BmcConstants.DefaultSupportButtonText);
+ public static readonly BindableProperty SupporterEmailProperty =
+ BindableProperty.Create(nameof(SupporterEmail), typeof(string), typeof(BuyMeACoffeeWidget),
+ null, propertyChanged: OnSupporterPropertyChanged);
+
+ public static readonly BindableProperty HideIfSupporterProperty =
+ BindableProperty.Create(nameof(HideIfSupporter), typeof(bool), typeof(BuyMeACoffeeWidget),
+ false, propertyChanged: OnSupporterPropertyChanged);
+
#endregion
#region Properties
@@ -94,6 +102,25 @@ public class BuyMeACoffeeWidget : ContentView
set => SetValue(SupportButtonTextProperty, value);
}
+ ///
+ /// Email address to check against your BMC supporters list.
+ /// Requires AccessToken configured via UseBuyMeACoffee(options => ...).
+ ///
+ public string? SupporterEmail
+ {
+ get => (string?)GetValue(SupporterEmailProperty);
+ set => SetValue(SupporterEmailProperty, value);
+ }
+
+ ///
+ /// When true, the control auto-hides if SupporterEmail is a verified supporter. Default: false.
+ ///
+ public bool HideIfSupporter
+ {
+ get => (bool)GetValue(HideIfSupporterProperty);
+ set => SetValue(HideIfSupporterProperty, value);
+ }
+
#endregion
/// Raised when the user taps the Support button, before opening the browser.
@@ -438,5 +465,11 @@ public class BuyMeACoffeeWidget : ContentView
widget._monthlyRow.IsVisible = (bool)newValue;
}
+ private static void OnSupporterPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is BuyMeACoffeeWidget widget)
+ BmcSupporterCheck.CheckAndHide(widget, widget.SupporterEmail, widget.HideIfSupporter);
+ }
+
#endregion
}
diff --git a/BuyMeCofee.Maui/Helpers/BmcBrandAssets.cs b/BuyMeCofee.Maui/Helpers/BmcBrandAssets.cs
index a10f61f..2bc75f1 100644
--- a/BuyMeCofee.Maui/Helpers/BmcBrandAssets.cs
+++ b/BuyMeCofee.Maui/Helpers/BmcBrandAssets.cs
@@ -1,18 +1,36 @@
+using System.Reflection;
+
namespace BuyMeCofee.Maui.Helpers;
internal static class BmcBrandAssets
{
- private const string LogoResourceName = "BuyMeCofee.Maui.Resources.Images.bmc_logo.png";
+ // MAUI converts bmc_logo.svg → bmc_logo.png at build time with proper DPI scaling
+ private const string LogoFileName = "bmc_logo.png";
+ // Fallback: embedded PNG for QR code overlay (SkiaSharp needs a raw stream)
+ private const string EmbeddedLogoName = "BuyMeCofee.Maui.Resources.Images.bmc_logo.png";
+
+ ///
+ /// Returns the cup logo as an ImageSource for MAUI Image controls.
+ /// Uses the MAUI asset pipeline (SVG → crisp multi-DPI PNG).
+ ///
internal static ImageSource GetCupLogo()
{
- var assembly = typeof(BmcBrandAssets).Assembly;
- return ImageSource.FromStream(() => assembly.GetManifestResourceStream(LogoResourceName)!);
+ return ImageSource.FromFile(LogoFileName);
}
- internal static Stream GetCupLogoStream()
+ ///
+ /// Returns the cup logo as a raw stream for SkiaSharp rendering (QR code overlay).
+ /// Falls back to embedded PNG resource.
+ ///
+ internal static Stream? GetCupLogoStream()
{
+ // Try embedded resource first (for QR code SkiaSharp rendering)
var assembly = typeof(BmcBrandAssets).Assembly;
- return assembly.GetManifestResourceStream(LogoResourceName)!;
+ var stream = assembly.GetManifestResourceStream(EmbeddedLogoName);
+ if (stream != null) return stream;
+
+ // If embedded PNG was removed, return null (QR code will render without logo)
+ return null;
}
}
diff --git a/BuyMeCofee.Maui/Helpers/BmcSupporterCheck.cs b/BuyMeCofee.Maui/Helpers/BmcSupporterCheck.cs
new file mode 100644
index 0000000..03ee6e3
--- /dev/null
+++ b/BuyMeCofee.Maui/Helpers/BmcSupporterCheck.cs
@@ -0,0 +1,26 @@
+namespace BuyMeCofee.Maui.Helpers;
+
+///
+/// Shared logic for controls to check supporter status and auto-hide.
+///
+internal static class BmcSupporterCheck
+{
+ public static async void CheckAndHide(ContentView control, string? email, bool hideIfSupporter)
+ {
+ if (!hideIfSupporter || string.IsNullOrWhiteSpace(email)) return;
+
+ var service = BmcConfiguration.SupporterService;
+ if (service == null) return;
+
+ try
+ {
+ var isSupporter = await service.IsSupporterAsync(email);
+ if (isSupporter)
+ control.IsVisible = false;
+ }
+ catch
+ {
+ // Silently fail — keep control visible
+ }
+ }
+}
diff --git a/BuyMeCofee.Maui/Resources/Images/bmc_logo.svg b/BuyMeCofee.Maui/Resources/Images/bmc_logo.svg
new file mode 100644
index 0000000..5ba6db9
--- /dev/null
+++ b/BuyMeCofee.Maui/Resources/Images/bmc_logo.svg
@@ -0,0 +1,16 @@
+
diff --git a/BuyMeCofee.Maui/Services/BmcSupporterService.cs b/BuyMeCofee.Maui/Services/BmcSupporterService.cs
new file mode 100644
index 0000000..52f9034
--- /dev/null
+++ b/BuyMeCofee.Maui/Services/BmcSupporterService.cs
@@ -0,0 +1,93 @@
+using System.Net.Http.Headers;
+using System.Text.Json;
+
+namespace BuyMeCofee.Maui.Services;
+
+///
+/// Checks the Buy Me a Coffee API to determine whether a given email
+/// belongs to a supporter (one-time or member). Results are cached.
+///
+public class BmcSupporterService
+{
+ private const string ApiBase = "https://developers.buymeacoffee.com/api/v1/";
+
+ private readonly HttpClient _http;
+ private readonly TimeSpan _cacheDuration;
+ private HashSet? _cachedEmails;
+ private DateTime _cacheExpiry;
+
+ public BmcSupporterService(string accessToken, TimeSpan cacheDuration)
+ {
+ _cacheDuration = cacheDuration;
+ _http = new HttpClient { BaseAddress = new Uri(ApiBase) };
+ _http.DefaultRequestHeaders.Authorization =
+ new AuthenticationHeaderValue("Bearer", accessToken);
+ }
+
+ ///
+ /// Returns true if the given email matches any one-time supporter
+ /// or active/inactive member on the creator's BMC account.
+ ///
+ public async Task IsSupporterAsync(string email)
+ {
+ if (string.IsNullOrWhiteSpace(email)) return false;
+
+ var emails = await GetAllSupporterEmailsAsync();
+ return emails.Contains(email.Trim());
+ }
+
+ private async Task> GetAllSupporterEmailsAsync()
+ {
+ if (_cachedEmails != null && DateTime.UtcNow < _cacheExpiry)
+ return _cachedEmails;
+
+ var emails = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ await FetchEmailsFromEndpoint("supporters", emails);
+ await FetchEmailsFromEndpoint("subscriptions", emails);
+
+ _cachedEmails = emails;
+ _cacheExpiry = DateTime.UtcNow + _cacheDuration;
+ return emails;
+ }
+
+ private async Task FetchEmailsFromEndpoint(string endpoint, HashSet emails)
+ {
+ try
+ {
+ var page = 1;
+ while (true)
+ {
+ var response = await _http.GetAsync($"{endpoint}?page={page}");
+ if (!response.IsSuccessStatusCode) break;
+
+ var json = await response.Content.ReadAsStringAsync();
+ using var doc = JsonDocument.Parse(json);
+ var root = doc.RootElement;
+
+ if (root.TryGetProperty("data", out var data))
+ {
+ foreach (var item in data.EnumerateArray())
+ {
+ if (item.TryGetProperty("payer_email", out var emailProp))
+ {
+ var email = emailProp.GetString();
+ if (!string.IsNullOrWhiteSpace(email))
+ emails.Add(email);
+ }
+ }
+ }
+
+ if (!root.TryGetProperty("next_page_url", out var nextPage) ||
+ nextPage.ValueKind == JsonValueKind.Null)
+ break;
+
+ page++;
+ }
+ }
+ catch
+ {
+ // Silently fail — don't block the app if BMC API is unreachable
+ }
+ }
+}
diff --git a/README.md b/README.md
index 7fa19eb..f18fe7a 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[](https://dotnet.microsoft.com/apps/maui)
[](https://dotnet.microsoft.com/apps/maui)
[](LICENSE)
-[](https://www.nuget.org)
+[](https://git.marketally.com/misc/bmc.maui/packages)
A comprehensive .NET MAUI library for integrating Buy Me a Coffee support into your cross-platform applications. Provides branded buttons, QR codes, and interactive widgets with official BMC styling and theming.
@@ -16,6 +16,7 @@ A comprehensive .NET MAUI library for integrating Buy Me a Coffee support into y
- [BuyMeACoffeeButton](#buymeacoffeebutton)
- [BuyMeACoffeeQrCode](#buymeacoffeeqrcode)
- [BuyMeACoffeeWidget](#buymeacoffeewidget)
+- [Supporter Verification](#supporter-verification)
- [Theming](#theming)
- [API Reference](#api-reference)
- [Platform Support](#platform-support)
@@ -26,14 +27,15 @@ A comprehensive .NET MAUI library for integrating Buy Me a Coffee support into y
## Features
-✅ **BuyMeACoffeeButton** - Branded button with official coffee cup logo and 8 color themes
-✅ **BuyMeACoffeeQrCode** - QR code generator with embedded BMC logo overlay
-✅ **BuyMeACoffeeWidget** - Full-featured support widget with amount selection, name/message fields, and monthly toggle
-✅ **Official Branding** - Uses authentic Buy Me a Coffee colors, logos, and styling
-✅ **Cross-Platform** - Supports Android, iOS, macOS, and Windows
-✅ **Customizable** - Extensive theming and styling options
-✅ **Touch Optimized** - Smooth animations and hover effects
-✅ **QR Code Generation** - High-quality QR codes with error correction level H
+- **BuyMeACoffeeButton** - Branded button with official coffee cup logo and 8 color themes
+- **BuyMeACoffeeQrCode** - QR code generator with embedded BMC logo overlay
+- **BuyMeACoffeeWidget** - Full-featured support widget with amount selection, name/message fields, and monthly toggle
+- **Official Branding** - Crisp SVG-based logos via MAUI asset pipeline, authentic Buy Me a Coffee colors and styling
+- **Supporter Verification** - Auto-hide controls for users who have already supported you via the BMC API
+- **Cross-Platform** - Supports Android, iOS, macOS, and Windows
+- **Customizable** - Extensive theming and styling options
+- **Touch Optimized** - Smooth animations and hover effects
+- **QR Code Generation** - High-quality QR codes with error correction level H
## Installation
@@ -41,19 +43,33 @@ A comprehensive .NET MAUI library for integrating Buy Me a Coffee support into y
- .NET 10.0 SDK or later
- .NET MAUI workload installed
-- Visual Studio 2022 17.8+ or Visual Studio Code with .NET MAUI extension
-### Add to Your Project
+### NuGet Package
-1. **Add the project reference** to your .NET MAUI application:
+Add the MarketAlly NuGet source to your `NuGet.config`:
```xml
-
-
-
+
+
+
+
+
+
```
-2. **Register the library** in your `MauiProgram.cs`:
+Then add the package reference:
+
+```xml
+
+```
+
+Or via CLI:
+
+```bash
+dotnet add package BuyMeCofee.Maui --version 1.1.0
+```
+
+### Register in MauiProgram.cs
```csharp
using BuyMeCofee.Maui;
@@ -66,7 +82,7 @@ public static class MauiProgram
builder
.UseMauiApp()
.UseBuyMeACoffee(); // Add this line
-
+
return builder.Build();
}
}
@@ -85,7 +101,7 @@ xmlns:bmc="clr-namespace:BuyMeCofee.Maui.Controls;assembly=BuyMeCofee.Maui"
Add a button to your page:
```xml
-
@@ -100,7 +116,7 @@ A branded button that opens your Buy Me a Coffee page in the default browser.
#### Basic Usage
```xml
-
```
@@ -108,7 +124,7 @@ A branded button that opens your Buy Me a Coffee page in the default browser.
#### With Custom Styling
```xml
-
```
-#### Code-Behind Example
-
-```csharp
-using BuyMeCofee.Maui.Controls;
-using BuyMeCofee.Maui.Enums;
-
-var button = new BuyMeACoffeeButton
-{
- Username = "yourname",
- ButtonText = "Buy me a coffee",
- Theme = BmcButtonTheme.Blue,
- CornerRadius = 10,
- FontSize = 16,
- CupSize = 28
-};
-
-layout.Children.Add(button);
-```
-
#### Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `Username` | `string` | `""` | Your Buy Me a Coffee username/slug |
| `ButtonText` | `string` | `"Buy me a coffee"` | Button label text |
-| `Theme` | `BmcButtonTheme` | `Yellow` | Color theme preset (Yellow, Black, White, Blue, Violet, Orange, Red, Green, Custom) |
+| `Theme` | `BmcButtonTheme` | `Yellow` | Color theme preset |
| `CustomBackgroundColor` | `Color?` | `null` | Background color (Custom theme only) |
| `CustomTextColor` | `Color?` | `null` | Text color (Custom theme only) |
| `CornerRadius` | `double` | `8.0` | Border corner radius |
| `FontSize` | `double` | `16.0` | Button text font size |
| `CupSize` | `double` | `28.0` | Coffee cup logo height |
+| `SupporterEmail` | `string?` | `null` | Email to check against supporters list |
+| `HideIfSupporter` | `bool` | `false` | Auto-hide if email is a verified supporter |
### BuyMeACoffeeQrCode
@@ -168,7 +167,7 @@ Generates a QR code linking to your BMC profile with the coffee cup logo overlai
#### Basic Usage
```xml
-
```
@@ -176,7 +175,7 @@ Generates a QR code linking to your BMC profile with the coffee cup logo overlai
#### With Custom Colors
```xml
-
```
-#### Code-Behind Example
-
-```csharp
-using BuyMeCofee.Maui.Controls;
-
-var qrCode = new BuyMeACoffeeQrCode
-{
- Username = "yourname",
- Size = 250,
- ForegroundColor = Colors.Black,
- BackgroundColor = Colors.White,
- LogoSizeFraction = 0.25
-};
-
-layout.Children.Add(qrCode);
-```
-
#### Properties
| Property | Type | Default | Description |
@@ -210,6 +192,8 @@ layout.Children.Add(qrCode);
| `ForegroundColor` | `Color` | `Black` | QR code module color |
| `BackgroundColor` | `Color` | `White` | QR code background color |
| `LogoSizeFraction` | `double` | `0.25` | Logo size as fraction of QR code (0.0-0.35) |
+| `SupporterEmail` | `string?` | `null` | Email to check against supporters list |
+| `HideIfSupporter` | `bool` | `false` | Auto-hide if email is a verified supporter |
### BuyMeACoffeeWidget
@@ -218,7 +202,7 @@ A full-featured support widget with amount entry, preset chips, name/message fie
#### Basic Usage
```xml
-
```
@@ -226,7 +210,7 @@ A full-featured support widget with amount entry, preset chips, name/message fie
#### With Custom Configuration
```xml
-
-```
-
```csharp
private void OnSupportRequested(object sender, SupportRequestedEventArgs e)
{
@@ -254,37 +230,9 @@ private void OnSupportRequested(object sender, SupportRequestedEventArgs e)
Console.WriteLine($"Amount: ${e.Amount}");
Console.WriteLine($"Message: {e.Message ?? "No message"}");
Console.WriteLine($"Monthly: {e.IsMonthly}");
-
- // Log analytics, show confirmation, etc.
}
```
-#### Code-Behind Example
-
-```csharp
-using BuyMeCofee.Maui.Controls;
-using BuyMeCofee.Maui.Models;
-
-var widget = new BuyMeACoffeeWidget
-{
- Username = "yourname",
- DisplayName = "John Doe",
- DefaultAmount = 5,
- SuggestedAmounts = new[] { 25, 50, 100 },
- AccentColor = Color.FromArgb("#6C5CE7"),
- ShowMonthlyOption = true,
- SupportButtonText = "Support"
-};
-
-widget.SupportRequested += (sender, e) =>
-{
- // Handle support request
- Debug.WriteLine($"Amount: ${e.Amount}, Monthly: {e.IsMonthly}");
-};
-
-layout.Children.Add(widget);
-```
-
#### Properties
| Property | Type | Default | Description |
@@ -296,6 +244,8 @@ layout.Children.Add(widget);
| `AccentColor` | `Color` | `#6C5CE7` | Support button background color |
| `ShowMonthlyOption` | `bool` | `true` | Show "Make this monthly" checkbox |
| `SupportButtonText` | `string` | `"Support"` | Support button label text |
+| `SupporterEmail` | `string?` | `null` | Email to check against supporters list |
+| `HideIfSupporter` | `bool` | `false` | Auto-hide if email is a verified supporter |
#### Events
@@ -308,19 +258,53 @@ layout.Children.Add(widget);
```csharp
public class SupportRequestedEventArgs : EventArgs
{
- public string Username { get; init; } // BMC username
- public int Amount { get; init; } // Support amount
- public string? Name { get; init; } // Supporter name (optional)
- public string? Message { get; init; } // Support message (optional)
- public bool IsMonthly { get; init; } // Monthly subscription toggle
+ public string Username { get; init; }
+ public int Amount { get; init; }
+ public string? Name { get; init; }
+ public string? Message { get; init; }
+ public bool IsMonthly { get; init; }
}
```
+## Supporter Verification
+
+The library can automatically check if a user has already supported you via the Buy Me a Coffee API. When verified, controls can auto-hide themselves — useful for removing donation prompts for existing supporters.
+
+### Setup
+
+Get your API access token from [Buy Me a Coffee Developer Dashboard](https://developers.buymeacoffee.com/).
+
+Configure the token in `MauiProgram.cs`:
+
+```csharp
+builder.UseBuyMeACoffee(options =>
+{
+ options.AccessToken = "your_bmc_api_token";
+ options.CacheDuration = TimeSpan.FromHours(1); // default
+});
+```
+
+### Usage
+
+Add `SupporterEmail` and `HideIfSupporter` to any control:
+
+```xml
+
+```
+
+When `HideIfSupporter` is `true` and the email matches a one-time supporter or active member, the control sets `IsVisible = false`.
+
+The service queries both `/api/v1/supporters` (one-time) and `/api/v1/subscriptions` (members) endpoints, caches results in memory, and fails silently if the API is unreachable.
+
## Theming
### Button Themes
-The library includes 8 official Buy Me a Coffee color themes:
+The library includes 8 official Buy Me a Coffee color themes plus a custom option:
```csharp
public enum BmcButtonTheme
@@ -337,38 +321,15 @@ public enum BmcButtonTheme
}
```
-### Theme Examples
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
### Brand Colors Reference
```csharp
public static class BmcColors
{
- public const string CupYellow = "#FFDD00"; // Official cup color
- public const string BrandDark = "#0D0C22"; // Brand dark
- public const string WidgetPurple = "#6C5CE7"; // Widget accent
- public const string WhiteStroke = "#E0E0E0"; // Border color
+ public const string CupYellow = "#FFDD00";
+ public const string BrandDark = "#0D0C22";
+ public const string WidgetPurple = "#6C5CE7";
+ public const string WhiteStroke = "#E0E0E0";
}
```
@@ -377,40 +338,20 @@ public static class BmcColors
### Extension Methods
```csharp
-public static class BuyMeACoffeeExtensions
-{
- ///
- /// Registers Buy Me a Coffee controls and their dependencies (SkiaSharp).
- /// Call this in your MauiProgram.cs CreateMauiApp() builder.
- ///
- public static MauiAppBuilder UseBuyMeACoffee(this MauiAppBuilder builder);
-}
+// Basic registration (no API features)
+public static MauiAppBuilder UseBuyMeACoffee(this MauiAppBuilder builder);
+
+// With API configuration (enables supporter verification)
+public static MauiAppBuilder UseBuyMeACoffee(this MauiAppBuilder builder, Action configure);
```
-### Helper Classes
-
-#### BmcThemeResolver
+### BmcOptions
```csharp
-public static class BmcThemeResolver
+public class BmcOptions
{
- ///
- /// Resolves a BmcButtonTheme enum to theme colors.
- ///
- /// Thrown for Custom theme
- public static BmcThemeInfo Resolve(BmcButtonTheme theme);
-}
-
-public record BmcThemeInfo(Color Background, Color TextColor, Color StrokeColor);
-```
-
-#### BmcBrandAssets
-
-```csharp
-internal static class BmcBrandAssets
-{
- internal static ImageSource GetCupLogo();
- internal static Stream GetCupLogoStream();
+ public string? AccessToken { get; set; }
+ public TimeSpan CacheDuration { get; set; } = TimeSpan.FromHours(1);
}
```
@@ -432,189 +373,33 @@ public static class BmcConstants
| Platform | Minimum Version | Status |
|----------|----------------|--------|
-| **Android** | API 21 (Android 5.0) | ✅ Supported |
-| **iOS** | 15.0+ | ✅ Supported |
-| **macOS** (Catalyst) | 15.0+ | ✅ Supported |
-| **Windows** | 10.0.17763.0+ | ✅ Supported |
-
-**Note:** On Linux, only Android targets are built by default. iOS and macOS Catalyst require macOS build environment.
+| **Android** | API 21 (Android 5.0) | Supported |
+| **iOS** | 15.0+ | Supported |
+| **macOS** (Catalyst) | 15.0+ | Supported |
+| **Windows** | 10.0.17763.0+ | Supported |
## Dependencies
-The library requires the following NuGet packages:
-
```xml
```
-These are automatically installed when you reference the project.
+## Support This Project
-## Examples
+If this library saved you time, consider buying me a coffee:
-### Complete Page Example
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### Dynamic Theme Switching
-
-```csharp
-using BuyMeCofee.Maui.Controls;
-using BuyMeCofee.Maui.Enums;
-
-public partial class ThemePage : ContentPage
-{
- private BuyMeACoffeeButton _button;
- private readonly BmcButtonTheme[] _themes =
- {
- BmcButtonTheme.Yellow,
- BmcButtonTheme.Black,
- BmcButtonTheme.Blue,
- BmcButtonTheme.Violet,
- BmcButtonTheme.Orange,
- BmcButtonTheme.Red,
- BmcButtonTheme.Green
- };
- private int _currentThemeIndex = 0;
-
- public ThemePage()
- {
- InitializeComponent();
-
- _button = new BuyMeACoffeeButton
- {
- Username = "yourname",
- Theme = _themes[0]
- };
-
- var switchButton = new Button { Text = "Change Theme" };
- switchButton.Clicked += OnSwitchTheme;
-
- var layout = new VerticalStackLayout
- {
- Spacing = 20,
- Padding = 20,
- Children = { _button, switchButton }
- };
-
- Content = layout;
- }
-
- private void OnSwitchTheme(object sender, EventArgs e)
- {
- _currentThemeIndex = (_currentThemeIndex + 1) % _themes.Length;
- _button.Theme = _themes[_currentThemeIndex];
- }
-}
-```
-
-### Analytics Integration
-
-```csharp
-using BuyMeCofee.Maui.Controls;
-using BuyMeCofee.Maui.Models;
-
-public partial class AnalyticsPage : ContentPage
-{
- public AnalyticsPage()
- {
- InitializeComponent();
-
- var widget = new BuyMeACoffeeWidget
- {
- Username = "yourname",
- DisplayName = "John Doe"
- };
-
- widget.SupportRequested += OnSupportRequested;
-
- Content = widget;
- }
-
- private void OnSupportRequested(object sender, SupportRequestedEventArgs e)
- {
- // Log to analytics service
- AnalyticsService.TrackEvent("SupportRequested", new Dictionary
- {
- { "username", e.Username },
- { "amount", e.Amount.ToString() },
- { "is_monthly", e.IsMonthly.ToString() },
- { "has_name", (!string.IsNullOrEmpty(e.Name)).ToString() },
- { "has_message", (!string.IsNullOrEmpty(e.Message)).ToString() }
- });
-
- // Show confirmation
- DisplayAlert("Thank You!",
- $"You're about to support with ${e.Amount}. Redirecting to Buy Me a Coffee...",
- "OK");
- }
-}
-```
+[](https://buymeacoffee.com/logikonline)
## Contributing
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.
-### Development Setup
-
-1. Clone the repository
-2. Open in Visual Studio 2022 or VS Code
-3. Ensure .NET MAUI workload is installed
-4. Build and run the sample project
-
## License
This project is licensed under the MIT License. See the LICENSE file for details.
---
-**Not affiliated with Buy Me a Coffee.** This is an unofficial community library. Buy Me a Coffee and its branding are trademarks of Buy Me a Coffee LLC.
\ No newline at end of file
+**Not affiliated with Buy Me a Coffee.** This is an unofficial community library. Buy Me a Coffee and its branding are trademarks of Buy Me a Coffee LLC.