Limited
2
0
Files
bmc.maui/BuyMeCofee.Maui/Controls/BuyMeACoffeeButton.cs
logikonline a719eb7975
All checks were successful
Release / Verify Apple Build (push) Successful in 12s
Release / Build & Pack (push) Successful in 8h59m32s
Release / Publish (push) Successful in 15s
fix(ci): load cup logo from embedded resource instead of maui asset
Replace MAUI asset pipeline logo loading with embedded resource stream to fix logo not appearing in consuming NuGet projects. Add CustomLogoSource property to Button and Widget controls for custom branding. Update logo to higher resolution PNG. Bump version to 1.1.2.
2026-03-04 02:37:46 -05:00

279 lines
9.5 KiB
C#

using BuyMeCofee.Maui.Constants;
using BuyMeCofee.Maui.Enums;
using BuyMeCofee.Maui.Helpers;
using Microsoft.Maui.Controls.Shapes;
namespace BuyMeCofee.Maui.Controls;
/// <summary>
/// A branded Buy Me a Coffee button with the official cup logo and 8 color theme presets.
/// Opens the user's BMC page in the default browser when tapped.
/// </summary>
public class BuyMeACoffeeButton : ContentView
{
#region Bindable Properties
public static readonly BindableProperty UsernameProperty =
BindableProperty.Create(nameof(Username), typeof(string), typeof(BuyMeACoffeeButton),
string.Empty);
public static readonly BindableProperty ButtonTextProperty =
BindableProperty.Create(nameof(ButtonText), typeof(string), typeof(BuyMeACoffeeButton),
BmcConstants.DefaultButtonText, propertyChanged: OnVisualPropertyChanged);
public static readonly BindableProperty ThemeProperty =
BindableProperty.Create(nameof(Theme), typeof(BmcButtonTheme), typeof(BuyMeACoffeeButton),
BmcButtonTheme.Yellow, propertyChanged: OnThemePropertyChanged);
public static readonly BindableProperty CustomBackgroundColorProperty =
BindableProperty.Create(nameof(CustomBackgroundColor), typeof(Color), typeof(BuyMeACoffeeButton),
null, propertyChanged: OnVisualPropertyChanged);
public static readonly BindableProperty CustomTextColorProperty =
BindableProperty.Create(nameof(CustomTextColor), typeof(Color), typeof(BuyMeACoffeeButton),
null, propertyChanged: OnVisualPropertyChanged);
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(BuyMeACoffeeButton),
BmcConstants.ButtonCornerRadius, propertyChanged: OnVisualPropertyChanged);
public static readonly BindableProperty FontSizeProperty =
BindableProperty.Create(nameof(FontSize), typeof(double), typeof(BuyMeACoffeeButton),
16.0, propertyChanged: OnVisualPropertyChanged);
public static readonly BindableProperty CupSizeProperty =
BindableProperty.Create(nameof(CupSize), typeof(double), typeof(BuyMeACoffeeButton),
28.0, propertyChanged: OnVisualPropertyChanged);
public static readonly BindableProperty CustomLogoSourceProperty =
BindableProperty.Create(nameof(CustomLogoSource), typeof(ImageSource), typeof(BuyMeACoffeeButton),
null, 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
/// <summary>Your Buy Me a Coffee username/slug.</summary>
public string Username
{
get => (string)GetValue(UsernameProperty);
set => SetValue(UsernameProperty, value);
}
/// <summary>Button label text. Default: "Buy me a coffee"</summary>
public string ButtonText
{
get => (string)GetValue(ButtonTextProperty);
set => SetValue(ButtonTextProperty, value);
}
/// <summary>Color theme preset. Default: Yellow (official BMC brand color).</summary>
public BmcButtonTheme Theme
{
get => (BmcButtonTheme)GetValue(ThemeProperty);
set => SetValue(ThemeProperty, value);
}
/// <summary>Custom background color. Only used when Theme is set to Custom.</summary>
public Color? CustomBackgroundColor
{
get => (Color?)GetValue(CustomBackgroundColorProperty);
set => SetValue(CustomBackgroundColorProperty, value);
}
/// <summary>Custom text color. Only used when Theme is set to Custom.</summary>
public Color? CustomTextColor
{
get => (Color?)GetValue(CustomTextColorProperty);
set => SetValue(CustomTextColorProperty, value);
}
/// <summary>Border corner radius. Default: 8</summary>
public double CornerRadius
{
get => (double)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
/// <summary>Font size for the button text. Default: 16</summary>
public double FontSize
{
get => (double)GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
/// <summary>Cup logo image height. Default: 28</summary>
public double CupSize
{
get => (double)GetValue(CupSizeProperty);
set => SetValue(CupSizeProperty, value);
}
/// <summary>Optional custom logo ImageSource. When set, replaces the default BMC cup logo.</summary>
public ImageSource? CustomLogoSource
{
get => (ImageSource?)GetValue(CustomLogoSourceProperty);
set => SetValue(CustomLogoSourceProperty, value);
}
/// <summary>
/// Email address to check against your BMC supporters list.
/// Requires AccessToken configured via UseBuyMeACoffee(options => ...).
/// </summary>
public string? SupporterEmail
{
get => (string?)GetValue(SupporterEmailProperty);
set => SetValue(SupporterEmailProperty, value);
}
/// <summary>
/// When true, the control auto-hides if SupporterEmail is a verified supporter. Default: false.
/// </summary>
public bool HideIfSupporter
{
get => (bool)GetValue(HideIfSupporterProperty);
set => SetValue(HideIfSupporterProperty, value);
}
#endregion
private Border _border = null!;
private Image _cupImage = null!;
private Label _textLabel = null!;
public BuyMeACoffeeButton()
{
BuildLayout();
ApplyTheme();
}
private void BuildLayout()
{
_cupImage = new Image
{
Source = BmcBrandAssets.GetCupLogo(),
HeightRequest = CupSize,
Aspect = Aspect.AspectFit,
VerticalOptions = LayoutOptions.Center,
};
_textLabel = new Label
{
Text = ButtonText,
FontSize = FontSize,
FontAttributes = FontAttributes.Bold,
VerticalOptions = LayoutOptions.Center,
};
var stack = new HorizontalStackLayout
{
Spacing = 8,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Children = { _cupImage, _textLabel }
};
_border = new Border
{
StrokeShape = new RoundRectangle { CornerRadius = new Microsoft.Maui.CornerRadius(CornerRadius) },
Stroke = Colors.Transparent,
Padding = new Thickness(16, 10),
Content = stack,
Shadow = new Shadow
{
Brush = new SolidColorBrush(Colors.Black),
Offset = new Point(0, 2),
Radius = 4,
Opacity = 0.25f,
}
};
var tap = new TapGestureRecognizer();
tap.Tapped += OnTapped;
_border.GestureRecognizers.Add(tap);
var hover = new PointerGestureRecognizer();
hover.PointerEntered += (_, _) => _border.Opacity = 0.85;
hover.PointerExited += (_, _) => _border.Opacity = 1.0;
_border.GestureRecognizers.Add(hover);
Content = _border;
}
private void ApplyTheme()
{
if (_border is null) return;
Color bg, text;
Color stroke = Colors.Transparent;
if (Theme == BmcButtonTheme.Custom)
{
bg = CustomBackgroundColor ?? Color.FromArgb(BmcColors.YellowBg);
text = CustomTextColor ?? Color.FromArgb(BmcColors.BrandDark);
}
else
{
var info = BmcThemeResolver.Resolve(Theme);
bg = info.Background;
text = info.TextColor;
stroke = info.StrokeColor;
}
_border.BackgroundColor = bg;
_border.Stroke = new SolidColorBrush(stroke);
_textLabel.TextColor = text;
}
private void UpdateVisuals()
{
if (_border is null) return;
_textLabel.Text = ButtonText;
_textLabel.FontSize = FontSize;
_cupImage.HeightRequest = CupSize;
_cupImage.Source = CustomLogoSource ?? BmcBrandAssets.GetCupLogo();
if (_border.StrokeShape is RoundRectangle rr)
rr.CornerRadius = new Microsoft.Maui.CornerRadius(CornerRadius);
ApplyTheme();
}
private async void OnTapped(object? sender, TappedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Username)) return;
await _border.ScaleToAsync(0.95, 80, Easing.CubicIn);
await _border.ScaleToAsync(1.0, 80, Easing.CubicOut);
await Launcher.Default.OpenAsync(new Uri($"{BmcConstants.BaseUrl}{Username}"));
}
private static void OnVisualPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is BuyMeACoffeeButton button)
button.UpdateVisuals();
}
private static void OnThemePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
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);
}
}