Add device detection and PDF generation features; refactor AuthDialog and update print utilities
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace FrontOffice.Main.Utilities;
|
||||
@@ -6,17 +5,25 @@ namespace FrontOffice.Main.Utilities;
|
||||
public class AuthDialogService
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly DialogOptions _maxWidth = new() { MaxWidth = MaxWidth.Medium, FullWidth = true, };
|
||||
private readonly IDeviceDetector _deviceDetector;
|
||||
private readonly DialogOptions _normalWidth = new() { MaxWidth = MaxWidth.ExtraSmall, FullWidth = true };
|
||||
|
||||
public AuthDialogService(IDialogService dialogService)
|
||||
public AuthDialogService(IDialogService dialogService, IDeviceDetector deviceDetector)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
ArgumentNullException.ThrowIfNull(dialogService);
|
||||
ArgumentNullException.ThrowIfNull(deviceDetector);
|
||||
_dialogService = dialogService!;
|
||||
_deviceDetector = deviceDetector!;
|
||||
}
|
||||
|
||||
public async Task ShowAuthDialogAsync()
|
||||
{
|
||||
|
||||
var dialog = await _dialogService.ShowAsync<Shared.AuthDialog>("ورود به حساب کاربری",_maxWidth);
|
||||
// Pick dialog size based on device type
|
||||
var options = _deviceDetector.IsMobile()
|
||||
? new DialogOptions() { FullScreen = true}
|
||||
: _normalWidth;
|
||||
|
||||
var dialog = await _dialogService.ShowAsync<Shared.AuthDialog>("ورود به حساب کاربری", options);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled)
|
||||
|
||||
117
src/FrontOffice.Main/Utilities/DeviceDetector.cs
Normal file
117
src/FrontOffice.Main/Utilities/DeviceDetector.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace FrontOffice.Main.Utilities;
|
||||
|
||||
public interface IDeviceDetector
|
||||
{
|
||||
DeviceInfo Detect(string? userAgent = null);
|
||||
bool IsMobile(string? userAgent = null);
|
||||
bool IsTablet(string? userAgent = null);
|
||||
bool IsDesktop(string? userAgent = null);
|
||||
}
|
||||
|
||||
public sealed class DeviceDetector : IDeviceDetector
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public DeviceDetector(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
|
||||
}
|
||||
|
||||
public DeviceInfo Detect(string? userAgent = null)
|
||||
{
|
||||
string? headerUa = null;
|
||||
var http = _httpContextAccessor.HttpContext;
|
||||
if (http != null)
|
||||
{
|
||||
headerUa = http.Request.Headers["User-Agent"].ToString();
|
||||
}
|
||||
|
||||
var ua = userAgent ?? headerUa ?? string.Empty;
|
||||
var uaLower = ua.ToLower(CultureInfo.InvariantCulture);
|
||||
|
||||
// Bots
|
||||
if (ContainsAny(uaLower, "bot", "spider", "crawler", "bingpreview", "facebookexternalhit"))
|
||||
{
|
||||
return new DeviceInfo(DeviceType.Bot, DetectOs(uaLower), DetectBrowser(uaLower), ua);
|
||||
}
|
||||
|
||||
var type = DetectDeviceType(uaLower);
|
||||
var os = DetectOs(uaLower);
|
||||
var browser = DetectBrowser(uaLower);
|
||||
|
||||
return new DeviceInfo(type, os, browser, ua);
|
||||
}
|
||||
|
||||
public bool IsMobile(string? userAgent = null) => Detect(userAgent).Type == DeviceType.Mobile;
|
||||
public bool IsTablet(string? userAgent = null) => Detect(userAgent).Type == DeviceType.Tablet;
|
||||
public bool IsDesktop(string? userAgent = null) => Detect(userAgent).Type == DeviceType.Desktop;
|
||||
|
||||
private static DeviceType DetectDeviceType(string uaLower)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(uaLower)) return DeviceType.Unknown;
|
||||
|
||||
// Tablets
|
||||
if (ContainsAny(uaLower, "ipad", "tablet", "kindle", "silk", "playbook"))
|
||||
return DeviceType.Tablet;
|
||||
|
||||
// Mobiles (exclude tablets when possible)
|
||||
if (ContainsAny(uaLower, "mobi", "iphone", "ipod", "android") && !uaLower.Contains("tablet"))
|
||||
return DeviceType.Mobile;
|
||||
|
||||
// Smart TV
|
||||
if (ContainsAny(uaLower, "smart-tv", "smarttv", "hbbtv", "appletv"))
|
||||
return DeviceType.Desktop; // treat TVs as desktop-like
|
||||
|
||||
return DeviceType.Desktop;
|
||||
}
|
||||
|
||||
private static string DetectOs(string uaLower)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(uaLower)) return "Unknown";
|
||||
if (uaLower.Contains("windows nt")) return "Windows";
|
||||
if (uaLower.Contains("android")) return "Android";
|
||||
if (uaLower.Contains("iphone") || uaLower.Contains("ipad") || uaLower.Contains("ipod") || uaLower.Contains("ios")) return "iOS";
|
||||
if (uaLower.Contains("mac os x") || uaLower.Contains("macintosh")) return "macOS";
|
||||
if (uaLower.Contains("linux")) return "Linux";
|
||||
if (uaLower.Contains("cros")) return "ChromeOS";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static string DetectBrowser(string uaLower)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(uaLower)) return "Unknown";
|
||||
if (uaLower.Contains("edg/")) return "Edge"; // Chromium Edge
|
||||
if (uaLower.Contains("edge/")) return "Edge"; // Legacy Edge
|
||||
if (uaLower.Contains("opr/") || uaLower.Contains("opera")) return "Opera";
|
||||
if (uaLower.Contains("chrome/") && !uaLower.Contains("edg/") && !uaLower.Contains("opr/") && !uaLower.Contains("chromium")) return "Chrome";
|
||||
if (uaLower.Contains("safari") && !uaLower.Contains("chrome")) return "Safari";
|
||||
if (uaLower.Contains("firefox")) return "Firefox";
|
||||
if (uaLower.Contains("msie") || uaLower.Contains("trident/")) return "IE";
|
||||
if (uaLower.Contains("chromium")) return "Chromium";
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private static bool ContainsAny(string source, params string[] values)
|
||||
{
|
||||
foreach (var v in values)
|
||||
{
|
||||
if (source.Contains(v)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DeviceType
|
||||
{
|
||||
Unknown = 0,
|
||||
Mobile = 1,
|
||||
Tablet = 2,
|
||||
Desktop = 3,
|
||||
Bot = 4
|
||||
}
|
||||
|
||||
public sealed record DeviceInfo(DeviceType Type, string Os, string Browser, string UserAgent);
|
||||
36
src/FrontOffice.Main/Utilities/Pdf/HtmlToPdfService.cs
Normal file
36
src/FrontOffice.Main/Utilities/Pdf/HtmlToPdfService.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using PdfSharpCore.Pdf;
|
||||
//using HtmlRendererCore.PdfSharpCore;
|
||||
|
||||
namespace FrontOffice.Main.Utilities.Pdf;
|
||||
|
||||
public interface IHtmlToPdfService
|
||||
{
|
||||
byte[] GeneratePdf(string html, string? fileName = null);
|
||||
}
|
||||
|
||||
public class HtmlToPdfService : IHtmlToPdfService
|
||||
{
|
||||
public byte[] GeneratePdf(string html, string? fileName = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(html))
|
||||
throw new ArgumentException("Html content is empty", nameof(html));
|
||||
|
||||
if (!html.Contains("<html", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
html = $"<html><head><meta charset='utf-8'><style>{GetDefaultStyles()}</style></head><body>{html}</body></html>";
|
||||
}
|
||||
else if (!html.Contains("<style", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Inject default styles if none provided
|
||||
html = html.Replace("<head>", "<head><style>" + GetDefaultStyles() + "</style>");
|
||||
}
|
||||
|
||||
using var document = new PdfDocument();
|
||||
//PdfGenerator.AddPdfPages(document, html, PdfSharpCore.PageSize.A4);
|
||||
using var ms = new MemoryStream();
|
||||
document.Save(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
private static string GetDefaultStyles() => @"body{font-family:'Vazir','Arial';direction:rtl;text-align:right;font-size:12px;line-height:1.6;margin:25px;} h1,h2,h3{margin:0 0 12px;} ul{margin:0 0 12px 0;padding:0 18px;} .small{font-size:10px;color:#555;}";
|
||||
}
|
||||
Reference in New Issue
Block a user