Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
QrXmppViewModel.cs
1using CommunityToolkit.Mvvm.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
6using System; // For StringComparison
7using System.IO; // For MemoryStream
8using Microsoft.Maui.Storage; // For FileSystem paths
9
11{
15 public abstract partial class QrXmppViewModel : XmppViewModel, ILinkableView
16 {
17 private readonly object qrSync = new();
18
22 protected QrXmppViewModel()
23 : base()
24 {
25 }
26
27
28 private string? qrTempFilePath;
29
34 public void GenerateQrCode(string Uri)
35 {
36 lock (this.qrSync)
37 {
38 // Idempotency: Avoid regenerating the same QR multiple times (mitigates race conditions during animations)
39 if (this.QrCode is not null && string.Equals(this.QrCodeUri, Uri, StringComparison.Ordinal))
40 {
41 ServiceRef.LogService.LogDebug($"GenerateQrCode skipped (same URI): {Uri}");
42 return;
43 }
44
45 if (this.QrCodeWidth == 0 || this.QrCodeHeight == 0)
46 {
47 this.QrCodeWidth = Constants.QrCode.DefaultImageWidth;
48 this.QrCodeHeight = Constants.QrCode.DefaultImageHeight;
49 }
50
51 if (this.QrResolutionScale == 0)
52 this.QrResolutionScale = Constants.QrCode.DefaultResolutionScale;
53
54 ServiceRef.LogService.LogDebug($"GenerateQrCode start: {Uri} ({this.QrCodeWidth}x{this.QrCodeHeight} scale={this.QrResolutionScale})");
55 byte[] Bin = Services.UI.QR.QrCode.GeneratePng(Uri, this.QrCodeWidth * this.QrResolutionScale, this.QrCodeHeight * this.QrResolutionScale);
56 this.QrCodeBin = Bin;
57
58 try
59 {
60 // Persist to a temporary file to avoid premature bitmap recycling when view hierarchies animate.
61 string CacheDir = FileSystem.CacheDirectory;
62 string FileName = $"qr_{Guid.NewGuid():N}.png";
63 string FullPath = Path.Combine(CacheDir, FileName);
64 File.WriteAllBytes(FullPath, Bin);
65 this.qrTempFilePath = FullPath;
66 this.QrCode = ImageSource.FromFile(FullPath);
67 }
68 catch (Exception Ex)
69 {
70 ServiceRef.LogService.LogException(Ex);
71 // Fallback to in-memory stream method if file write fails
72 this.QrCode = ImageSource.FromStream(() => new MemoryStream(Bin));
73 }
74
75 this.QrCodeContentType = Constants.MimeTypes.Png;
76 this.QrCodeUri = Uri;
77 this.HasQrCode = true;
78 ServiceRef.LogService.LogDebug($"GenerateQrCode done: {Uri} (bytes={Bin.Length})");
79 }
80 }
81
85 public void RemoveQrCode()
86 {
87 lock (this.qrSync)
88 {
89 this.QrCode = null;
90 this.QrCodeBin = null;
91 this.QrCodeContentType = null;
92 this.QrCodeUri = null;
93 this.HasQrCode = false;
94 if (!string.IsNullOrEmpty(this.qrTempFilePath))
95 {
96 try
97 {
98 if (File.Exists(this.qrTempFilePath))
99 File.Delete(this.qrTempFilePath);
100 }
101 catch (Exception Ex)
102 {
103 ServiceRef.LogService.LogException(Ex);
104 }
105 finally
106 {
107 this.qrTempFilePath = null;
108 }
109 }
110 }
111 }
112
117 [RelayCommand]
118 public async Task OpenQrPopup(string? Title)
119 {
120 if (this.QrCodeBin is null) return;
121
122 ShowQRPopup QrPopup;
123
124 if (!string.IsNullOrEmpty(Title))
125 QrPopup = new(this.QrCodeBin, this.QrCodeUri, Title);
126 else
127 QrPopup = new(this.QrCodeBin, this.QrCodeUri);
128
129 await ServiceRef.PopupService.PushAsync(QrPopup);
130 }
131
132 #region Properties
133
137 [ObservableProperty]
138 private ImageSource? qrCode;
139
143 [ObservableProperty]
144 private bool hasQrCode;
145
149 [ObservableProperty]
150 private string? qrCodeUri;
151
155 [ObservableProperty]
156 private int qrCodeWidth;
157
161 [ObservableProperty]
162 private int qrCodeHeight;
163
169 [ObservableProperty]
170 private int qrResolutionScale;
171
175 [ObservableProperty]
176 private byte[]? qrCodeBin;
177
181 [ObservableProperty]
182 private string? qrCodeContentType;
183
184
185
186 #endregion
187
188 #region ILinkableView
189
193 public virtual bool IsLinkable => this.HasQrCode;
194
198 public virtual bool EncodeAppLinks => true;
199
203 public virtual string? Link => this.QrCodeUri;
204
208 public abstract Task<string> Title { get; }
209
213 public virtual bool HasMedia => this.HasQrCode;
214
218 public virtual byte[]? Media => this.QrCodeBin;
219
223 public virtual string? MediaContentType => this.QrCodeContentType;
224
225 #endregion
226 }
227}
const string Png
The PNG MIME type.
Definition: Constants.cs:296
const int DefaultImageHeight
The default height to use when generating QR Code images.
Definition: Constants.cs:1013
const int DefaultImageWidth
The default width to use when generating QR Code images.
Definition: Constants.cs:1009
const int DefaultResolutionScale
The default scale factor to apply to the QR Code image resolution.
Definition: Constants.cs:1017
A set of never changing property constants and helpful values.
Definition: Constants.cs:24
Base class that references services in the app.
Definition: ServiceRef.cs:43
static ILogService LogService
Log service.
Definition: ServiceRef.cs:214
static IPopupService PopupService
Popup service for presenting application popups.
Definition: ServiceRef.cs:142
A view model that holds the XMPP state.
virtual bool HasMedia
If linkable view has media associated with link.
QrXmppViewModel()
Creates an instance of a XmppViewModel.
virtual bool IsLinkable
If the current view is linkable.
virtual ? string MediaContentType
Content-Type of associated media.
abstract Task< string > Title
Title of the current view
void RemoveQrCode()
Removes the QR-code
async Task OpenQrPopup(string? Title)
Open QR Popup
void GenerateQrCode(string Uri)
Generates a QR-code
virtual ? byte[] Media
Encoded media, if available.
virtual bool EncodeAppLinks
If App links should be encoded with the link.
A view model that holds the XMPP state.
Interface for linkable views.
Definition: ILinkableView.cs:7