Skip to content

Commit 9554156

Browse files
committed
Added Media Library
1 parent 6128196 commit 9554156

16 files changed

Lines changed: 1004 additions & 313 deletions

SpawnDev.MatrixLEDDisplay.Demo/Pages/Home.razor

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
@page "/"
22
@using SpawnDev.BlazorJS
33
@using SpawnDev.BlazorJS.JSObjects
4+
@using SpawnDev.BlazorJS.Toolbox
5+
@using SpawnDev.MatrixLEDDisplay.Demo.Services
46
@implements IAsyncDisposable
57

68
<div>
@@ -9,35 +11,22 @@
911
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="0">
1012
<div style="width: 100%;">
1113
<RadzenMenu>
12-
<RadzenMenuItem title="Connect MI Matrix Display" Text="MI Matrix Display" Icon="add" Click="@(() => ConnectClick())" />
14+
<RadzenMenuItem title="Add image(s) to media library" Text="Image" Icon="add" Click="@(() => AddImages())" />
15+
<RadzenMenuItem title="Connect MI Matrix Display" Text="Display" Icon="add" Click="@(() => ConnectClick())" />
1316
</RadzenMenu>
1417
</div>
1518
</RadzenStack>
1619
</div>
1720
<div>
18-
<RadzenTabs RenderMode="TabRenderMode.Client" >
21+
<RadzenTabs RenderMode="TabRenderMode.Client">
1922
<Tabs>
20-
@* <RadzenTabsItem Text="Test">
21-
<div>
22-
<select @onchange=SelectDisplay_OnChange style="min-width: 23rem;">
23-
<option selected="@(_selectedDisplayDeviceIdHex == "")" value="">None</option>
24-
@foreach (var display in DisplayService.Displays)
25-
{
26-
<option selected="@(_selectedDisplayDeviceIdHex == display.DeviceHexId)" value="@display.DeviceHexId">@display.DeviceHexId</option>
27-
}
28-
</select>
29-
<button disabled="@(SelectedDisplay == null)" @onclick=Send>Send</button>
30-
</div>
31-
<div>
32-
<textarea @bind=_textArea cols="50" rows="10"></textarea>
33-
</div>
34-
<div>
35-
Status: @_testStatus
36-
</div>
37-
</RadzenTabsItem> *@
38-
@foreach (var display in DisplayService.Displays)
23+
<RadzenTabsItem Text="Media Library">
24+
<MediaLibraryViewer />
25+
</RadzenTabsItem>
26+
@foreach (var display in DisplayService.Displays.Values)
3927
{
40-
<RadzenTabsItem Text="@display.DeviceHexId">
28+
var metadata = DisplayService.GetMetaData(display);
29+
<RadzenTabsItem Text="@metadata!.Name">
4130
<MatrixDisplayControl Display="@display" />
4231
</RadzenTabsItem>
4332
}
@@ -48,6 +37,13 @@
4837

4938
@code
5039
{
40+
41+
[Inject]
42+
MediaLibraryManager MediaLibraryManager { get; set; } = default!;
43+
44+
[Inject]
45+
NotificationService NotificationService { get; set; } = default!;
46+
5147
[Inject]
5248
BlazorJSRuntime JS { get; set; } = default!;
5349

@@ -67,7 +63,7 @@
6763
void SelectDisplay_OnChange(ChangeEventArgs args)
6864
{
6965
var deviceIdHex = args.Value as string;
70-
var selectedDisplay = DisplayService.Displays.FirstOrDefault(o => o.DeviceHexId == deviceIdHex);
66+
var selectedDisplay = DisplayService.Displays.Values.FirstOrDefault(o => o.DeviceHexId == deviceIdHex);
7167
SelectedDisplay = selectedDisplay;
7268
JS.Log("SelectedDisplay", SelectedDisplay?.DeviceHexId);
7369
StateHasChanged();
@@ -112,7 +108,7 @@
112108
}
113109
async Task LoadPenguinImage(MIMatrixDisplay display, bool save)
114110
{
115-
await display.LoadImageFromURL("pixelart/penguin_16.png", save);
111+
await display.SendImage("pixelart/penguin_16.png", save);
116112
}
117113
protected override void OnAfterRender(bool firstRender)
118114
{
@@ -140,18 +136,32 @@
140136
display.BackgroundColor.HexColor = newColor!;
141137
}
142138
bool _connecting = false;
139+
140+
async Task AddImages()
141+
{
142+
await MediaLibraryManager.ShowImageSelectDialog();
143+
}
143144
async Task ConnectClick()
144145
{
145146
if (_connecting || !isWebBluetoothEnabled) return;
146147
_connecting = true;
147148
bleState = "Connecting...";
148149
StateHasChanged();
150+
MIMatrixDisplay? display = null;
149151
try
150152
{
151-
await DisplayService.ConnectDisplay();
153+
display = await DisplayService.ConnectDisplay();
152154
}
153155
finally
154156
{
157+
if (display == null)
158+
{
159+
NotificationService.Notify(NotificationSeverity.Warning, "Connect was cancelled or failed");
160+
}
161+
else
162+
{
163+
NotificationService.Notify(NotificationSeverity.Success, "Display connected");
164+
}
155165
_connecting = false;
156166
bleState = "Ready";
157167
StateHasChanged();

SpawnDev.MatrixLEDDisplay.Demo/Pages/MatrixDisplayControl.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77
<div class="display-control">
88
<div>
9-
<RadzenMenu>
9+
<RadzenMenu Responsive="false">
1010
<RadzenMenuItem Icon="close" disabled="@(Busy)" title="Disconnect" Click=@(() => DisplayService.RemoveDisplay(Display)) />
1111
@* <RadzenMenuItem Icon="transition_dissolve" title="Test pattern" disabled="@(!display.Connected)" Click=@(() => display.SendTestPicture(true)) />
1212
<RadzenMenuItem Icon="image" title="Test image" disabled="@(!display.Connected)" Click=@(() => LoadPenguinImage(display, true)) /> *@
13-
<RadzenMenuItem Icon="file_open" title="Load image file" disabled="@(!Display.Connected || Busy)" Click=@(() => Display.SelectAndLoadImage(true)) />
13+
<RadzenMenuItem Icon="file_open" title="Load image file" disabled="@(!Display.Connected || Busy)" Click=@(() => Display.SelectAndSendImage(true)) />
1414
<RadzenMenuItem Icon="replay" title="Reset" disabled="@(!Display.Connected || Busy)" Click=@(() => Display.Reset()) />
1515
<RadzenMenuItem Icon="power_off" title="Power off" disabled="@(!Display.Connected || Busy)" Click=@(() => Display.PowerOff()) />
1616
<RadzenMenuItem Icon="power" title="Power on" disabled="@(!Display.Connected || Busy)" Click=@(() => Display.PowerOn()) />
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
@using SpawnDev.MatrixLEDDisplay.Demo.Services
2+
<div class="media-library-item" style="cursor: pointer; @_Style" tabindex="0" @onclick="OnClick" @onclick:preventDefault="true" @oncontextmenu="OnContext" @oncontextmenu:preventDefault="true">
3+
<div>
4+
<img width="100" height="100" src="@ImageSrc" />
5+
</div>
6+
<div style="height: 5rem;">
7+
@Name
8+
</div>
9+
</div>
10+
11+
@code {
12+
[Parameter]
13+
public MediaLibraryItem MediaLibraryItem { get; set; }
14+
15+
public string Name => MediaLibraryItem?.Name.Trim('/') ?? "";
16+
17+
public string ImageSrc => MediaLibraryItem?.Url ?? "";
18+
19+
[Parameter]
20+
public string Style { get; set; } = "";
21+
22+
string _Style => $"{(Selected ? "background-color: orange;" : "")}{Style}";
23+
24+
[Parameter]
25+
public bool Selected { get; set; }
26+
27+
[Inject]
28+
ContextMenuService ContextMenuService { get; set; } = default!;
29+
30+
[Inject]
31+
DialogService DialogService { get; set; } = default!;
32+
33+
[Inject]
34+
NotificationService NotificationService { get; set; } = default!;
35+
36+
[Inject]
37+
MediaLibraryManager MediaLibraryManager { get; set; } = default!;
38+
39+
[Inject]
40+
MatrixLEDDisplayService MatrixLEDDisplayService { get; set; } = default!;
41+
42+
void OnClick(MouseEventArgs args)
43+
{
44+
ShowContextMenuWithItems(args);
45+
}
46+
void OnContext(MouseEventArgs args)
47+
{
48+
ShowContextMenuWithItems(args);
49+
}
50+
void ShowContextMenuWithItems(MouseEventArgs args)
51+
{
52+
var options = new List<ContextMenuItem>();
53+
foreach (var display in MatrixLEDDisplayService.Displays.Values)
54+
{
55+
var metadata = MatrixLEDDisplayService.GetMetaData(display);
56+
options.Add(new ContextMenuItem()
57+
{
58+
Text = $"Send to: {metadata!.Name}",
59+
Icon = "home",
60+
Value = async () =>
61+
{
62+
try
63+
{
64+
await display.SendImage(ImageSrc, true);
65+
NotificationService.Notify(NotificationSeverity.Success, "Image set");
66+
}
67+
catch
68+
{
69+
NotificationService.Notify(NotificationSeverity.Error, "Image set failed");
70+
}
71+
},
72+
});
73+
}
74+
options.Add(new ContextMenuItem()
75+
{
76+
Text = $"Remove",
77+
Icon = "delete",
78+
Value = async () =>
79+
{
80+
var confirm = await DialogService.Confirm($"Remove from media library? {Name}", "Are you sure?");
81+
if (confirm != true) return;
82+
await MediaLibraryManager.Remove(MediaLibraryItem.Name);
83+
},
84+
});
85+
ContextMenuService.Open(args, options, OnMenuItemClick);
86+
}
87+
void OnMenuItemClick(MenuItemEventArgs args)
88+
{
89+
ContextMenuService.Close();
90+
try
91+
{
92+
93+
if (args.Value is Action action) action();
94+
else if (args.Value is Func<Task> func) _ = func();
95+
}
96+
catch (Exception ex)
97+
{
98+
var nmt = true;
99+
}
100+
}
101+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.media-library-item {
2+
background-color: transparent;
3+
border-radius: 0.5rem;
4+
}
5+
6+
.media-library-item:hover {
7+
background-color: #80808080;
8+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
@using SpawnDev.MatrixLEDDisplay.Demo.Services
2+
@implements IDisposable
3+
4+
<div>
5+
@foreach (var mediaItem in MediaLibraryManager.MediaItems)
6+
{
7+
<MediaItem MediaLibraryItem="mediaItem" Style="display: inline-block; padding: 6px; width: 112px; overflow: hidden;" />
8+
}
9+
</div>
10+
11+
@code {
12+
[Inject]
13+
MediaLibraryManager MediaLibraryManager { get; set; } = default!;
14+
15+
bool _beenInit = false;
16+
protected override void OnInitialized()
17+
{
18+
if (!_beenInit)
19+
{
20+
_beenInit = true;
21+
MediaLibraryManager.OnStateChanged += MediaLibraryManager_OnStateChanged;
22+
}
23+
}
24+
void MediaLibraryManager_OnStateChanged()
25+
{
26+
StateHasChanged();
27+
}
28+
public void Dispose()
29+
{
30+
if (_beenInit)
31+
{
32+
_beenInit = false;
33+
MediaLibraryManager.OnStateChanged -= MediaLibraryManager_OnStateChanged;
34+
}
35+
}
36+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
@implements IDisposable
2+
3+
<div>
4+
<RadzenStack Gap="1rem">
5+
<div style="display: flex; flex-direction: row; justify-content:space-around;">
6+
<div style="display: inline-block; padding: 6px; width: 112px; overflow: hidden;">
7+
<div>New file</div>
8+
<div>
9+
<img height="100" width="100" src="@newFileUrl" />
10+
</div>
11+
</div>
12+
<div style="display: inline-block; padding: 6px; width: 112px; overflow: hidden;">
13+
<div>Old file</div>
14+
<div>
15+
<img height="100" width="100" src="@oldFileUrl" />
16+
</div>
17+
</div>
18+
</div>
19+
<div style="height: 4rem; text-align: center;">
20+
@NewFile?.Name
21+
</div>
22+
</RadzenStack>
23+
<RadzenStack Orientation="Orientation.Horizontal" JustifyContent="JustifyContent.Center" Gap="1rem" class="rz-mt-8 rz-mb-4">
24+
<RadzenButton Variant=Variant.Outlined Icon="undo" Text="Overwrite" Click=@Overwrite />
25+
<RadzenButton Variant=Variant.Outlined Icon="undo" Text="Keep Both" Click=@KeepBoth />
26+
<RadzenButton Variant=Variant.Outlined Icon="cancel" Text="Skip" Click="@Skip" />
27+
</RadzenStack>
28+
</div>
29+
30+
@code {
31+
[Inject]
32+
DialogService DialogService { get; set; } = default!;
33+
void Overwrite()
34+
{
35+
DialogService.Close(HandleExists.Overwrite);
36+
}
37+
void KeepBoth()
38+
{
39+
DialogService.Close(HandleExists.KeepBoth);
40+
}
41+
void Skip()
42+
{
43+
DialogService.Close(HandleExists.Skip);
44+
}
45+
public enum HandleExists
46+
{
47+
Overwrite,
48+
KeepBoth,
49+
Skip,
50+
}
51+
string newFileUrl;
52+
string oldFileUrl;
53+
54+
[Parameter]
55+
public File NewFile { get; set; }
56+
57+
[Parameter]
58+
public Blob OldFile { get; set; }
59+
60+
bool beenInit = false;
61+
62+
protected override void OnInitialized()
63+
{
64+
if (!beenInit)
65+
{
66+
beenInit = true;
67+
newFileUrl = URL.CreateObjectURL(NewFile);
68+
oldFileUrl = URL.CreateObjectURL(OldFile);
69+
}
70+
}
71+
public void Dispose()
72+
{
73+
if (beenInit)
74+
{
75+
beenInit = false;
76+
URL.RevokeObjectURL(newFileUrl);
77+
URL.RevokeObjectURL(oldFileUrl);
78+
}
79+
}
80+
public static async Task<HandleExists> Show(DialogService dialogService, File newFile, Blob oldFile)
81+
{
82+
HandleExists restoreOptions = await dialogService.OpenAsync<ResolveFileExistsDialog>($"Target exists", new Dictionary<string, object>
83+
{
84+
{ "NewFile", newFile },
85+
{ "OldFile", oldFile }
86+
},
87+
new DialogOptions()
88+
{
89+
CloseDialogOnEsc = false,
90+
ShowClose = false,
91+
CloseDialogOnOverlayClick = false,
92+
});
93+
return restoreOptions;
94+
}
95+
}

0 commit comments

Comments
 (0)