Skip to content

Commit 62190c2

Browse files
committed
refactored
1 parent 556b754 commit 62190c2

20 files changed

+955
-103
lines changed

Component/AppBar.razor

Lines changed: 9 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
@inject Data Datas
1+
@using TomAndJerry.Services
2+
@inject IStateService StateService
23
@inject NavigationManager nav
34
@inject IJSRuntime JSRuntime
45

@@ -26,37 +27,22 @@
2627

2728
<!-- Center Section - Search -->
2829
<div class="flex-1 max-w-2xl mx-8">
29-
<div class="relative">
30-
<div class="flex items-center bg-white border border-gray-300 rounded-full shadow-sm hover:shadow-md transition-shadow">
31-
<input type="text"
32-
name="text"
33-
@bind-value="@FindData"
34-
class="flex-1 px-4 py-2 text-sm border-0 rounded-l-full focus:outline-none focus:ring-0"
35-
placeholder="Search Tom & Jerry episodes...">
36-
37-
<button class="px-4 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-r-full transition-colors"
38-
@onclick="GoTOPage">
39-
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
40-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
41-
</svg>
42-
</button>
43-
</div>
44-
</div>
30+
<RealTimeSearch />
4531
</div>
4632

4733
<!-- Right Section -->
4834
<div class="flex items-center space-x-2">
4935
<!-- Stats -->
5036
<div class="hidden md:flex items-center space-x-4 text-sm text-gray-600">
5137
<div class="text-center">
52-
@if (Datas.VideosData.Count == 0)
38+
@if (StateService.IsLoading || !StateService.CurrentVideos.Any())
5339
{
5440
<div class="h-5 bg-gray-200 rounded w-8 mx-auto mb-1 skeleton"></div>
5541
<div class="text-xs text-gray-500">Episodes</div>
5642
}
5743
else
5844
{
59-
<div class="font-semibold text-gray-900">@Datas.VideosData.Count</div>
45+
<div class="font-semibold text-gray-900">@StateService.CurrentVideos.Count()</div>
6046
<div class="text-xs text-gray-500">Episodes</div>
6147
}
6248
</div>
@@ -72,63 +58,21 @@
7258
</header>
7359

7460
@code {
75-
public string FindData = string.Empty;
76-
7761
void GoToHomePage()
7862
{
7963
nav.NavigateTo("");
8064
}
8165

82-
public void GoTOPage()
83-
{
84-
Filter();
85-
if (!string.IsNullOrEmpty(FindData))
86-
{
87-
nav.NavigateTo($"Search/{FindData}");
88-
}
89-
}
90-
91-
protected override async Task OnAfterRenderAsync(bool firstRender)
66+
protected override void OnInitialized()
9267
{
93-
if (firstRender)
94-
{
95-
await JSRuntime.InvokeVoidAsync("blazorKeyPressed", DotNetObjectReference.Create(this));
96-
}
68+
StateService.OnStateChanged += StateHasChanged;
9769
}
9870

99-
[JSInvokable]
100-
public void OnArrowKeyPressed(string key)
101-
{
102-
Filter();
103-
if (key == "Enter" && !string.IsNullOrEmpty(FindData))
104-
{
105-
nav.NavigateTo($"Search/{FindData}");
106-
}
107-
108-
StateHasChanged();
109-
}
110-
111-
void Filter()
71+
public void Dispose()
11272
{
113-
Datas.FilteredData = Datas.VideosData.Where(x => x.Description != null && x.Description.Contains(FindData, StringComparison.OrdinalIgnoreCase))
114-
.ToList();
73+
StateService.OnStateChanged -= StateHasChanged;
11574
}
116-
11775
}
11876

11977

12078

121-
@code{
122-
123-
protected override async Task OnInitializedAsync()
124-
{
125-
Datas.OnChange += StateHasChanged;
126-
await Datas.InitializeAsync();
127-
}
128-
129-
public void Dispose()
130-
{
131-
Datas.OnChange -= StateHasChanged;
132-
}
133-
134-
}

Component/BaseComponent.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using TomAndJerry.Services;
2+
using Microsoft.AspNetCore.Components;
3+
4+
namespace TomAndJerry.Component;
5+
6+
public abstract class BaseComponent : ComponentBase, IDisposable
7+
{
8+
[Inject] protected IStateService StateService { get; set; } = null!;
9+
10+
protected override async Task OnInitializedAsync()
11+
{
12+
StateService.OnStateChanged += StateHasChanged;
13+
await OnComponentInitializedAsync();
14+
}
15+
16+
protected virtual async Task OnComponentInitializedAsync()
17+
{
18+
await Task.CompletedTask;
19+
}
20+
21+
public virtual void Dispose()
22+
{
23+
StateService.OnStateChanged -= StateHasChanged;
24+
}
25+
}

Component/Giscus.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
public string EmitMetadata { get; set; } = "0";
3030
public string Strict { get; set; } = "0";
3131

32-
private RenderFragment Script { get; set; }
32+
private RenderFragment Script { get; set; } = null!;
3333

3434
protected override void OnParametersSet()
3535
{

Component/RealTimeSearch.razor

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
@using TomAndJerry.Services
2+
@inject ISearchService SearchService
3+
@inject IVideoService VideoService
4+
@inject NavigationManager NavigationManager
5+
@inject IJSRuntime JSRuntime
6+
7+
<div class="relative">
8+
<div class="flex items-center bg-white border border-gray-300 rounded-full shadow-sm hover:shadow-md transition-shadow">
9+
<input type="text"
10+
@bind="searchTerm"
11+
@bind:event="oninput"
12+
@onkeydown="HandleKeyDown"
13+
class="flex-1 px-4 py-2 text-sm border-0 rounded-l-full focus:outline-none focus:ring-0"
14+
placeholder="Search Tom & Jerry episodes...">
15+
16+
<button class="px-4 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-r-full transition-colors"
17+
@onclick="PerformSearch">
18+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
19+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
20+
</svg>
21+
</button>
22+
</div>
23+
24+
<!-- Search Suggestions Dropdown -->
25+
@if (showSuggestions && searchSuggestions.Any())
26+
{
27+
<div class="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 max-h-60 overflow-y-auto">
28+
@foreach (var suggestion in searchSuggestions)
29+
{
30+
<button class="w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 transition-colors"
31+
@onclick="() => SelectSuggestion(suggestion)">
32+
<div class="flex items-center space-x-2">
33+
<svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
34+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
35+
</svg>
36+
<span>@suggestion</span>
37+
</div>
38+
</button>
39+
}
40+
</div>
41+
}
42+
43+
<!-- Search Results Count -->
44+
@if (!string.IsNullOrEmpty(searchTerm) && searchResults.Any())
45+
{
46+
<div class="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-40 p-3">
47+
<div class="flex items-center justify-between text-sm text-gray-600">
48+
<span>@searchResults.Count() results found</span>
49+
<button class="text-red-600 hover:text-red-800 font-medium"
50+
@onclick="NavigateToSearchResults">
51+
View all results
52+
</button>
53+
</div>
54+
</div>
55+
}
56+
</div>
57+
58+
@code {
59+
private string searchTerm = string.Empty;
60+
private IEnumerable<string> searchSuggestions = Enumerable.Empty<string>();
61+
private IEnumerable<TomAndJerry.Model.Video> searchResults = Enumerable.Empty<TomAndJerry.Model.Video>();
62+
private bool showSuggestions = false;
63+
private Timer? searchTimer;
64+
65+
protected override void OnInitialized()
66+
{
67+
SearchService.OnSearchResultsChanged += OnSearchResultsChanged;
68+
69+
// Initialize search timer for debouncing
70+
searchTimer = new Timer(async _ => await DebouncedSearch(), null, Timeout.Infinite, Timeout.Infinite);
71+
}
72+
73+
private void OnSearchResultsChanged(IEnumerable<TomAndJerry.Model.Video> results)
74+
{
75+
searchResults = results;
76+
InvokeAsync(StateHasChanged);
77+
}
78+
79+
private async Task DebouncedSearch()
80+
{
81+
if (string.IsNullOrWhiteSpace(searchTerm))
82+
{
83+
searchSuggestions = Enumerable.Empty<string>();
84+
searchResults = Enumerable.Empty<TomAndJerry.Model.Video>();
85+
showSuggestions = false;
86+
await InvokeAsync(StateHasChanged);
87+
return;
88+
}
89+
90+
// Get search suggestions
91+
searchSuggestions = await SearchService.GetSearchSuggestionsAsync(searchTerm);
92+
93+
// Perform search
94+
await SearchService.SearchAsync(searchTerm);
95+
96+
showSuggestions = searchSuggestions.Any();
97+
await InvokeAsync(StateHasChanged);
98+
}
99+
100+
private async Task HandleKeyDown(KeyboardEventArgs e)
101+
{
102+
if (e.Key == "Enter")
103+
{
104+
PerformSearch();
105+
}
106+
else if (e.Key == "Escape")
107+
{
108+
showSuggestions = false;
109+
await InvokeAsync(StateHasChanged);
110+
}
111+
else
112+
{
113+
// Debounce search input
114+
searchTimer?.Change(300, Timeout.Infinite);
115+
}
116+
}
117+
118+
private void PerformSearch()
119+
{
120+
if (!string.IsNullOrEmpty(searchTerm))
121+
{
122+
NavigationManager.NavigateTo($"Search/{Uri.EscapeDataString(searchTerm)}");
123+
}
124+
showSuggestions = false;
125+
}
126+
127+
private void SelectSuggestion(string suggestion)
128+
{
129+
searchTerm = suggestion;
130+
showSuggestions = false;
131+
PerformSearch();
132+
}
133+
134+
private void NavigateToSearchResults()
135+
{
136+
if (!string.IsNullOrEmpty(searchTerm))
137+
{
138+
NavigationManager.NavigateTo($"Search/{Uri.EscapeDataString(searchTerm)}");
139+
}
140+
}
141+
142+
public void Dispose()
143+
{
144+
SearchService.OnSearchResultsChanged -= OnSearchResultsChanged;
145+
searchTimer?.Dispose();
146+
}
147+
}

Component/VideoGrid.razor

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
@using TomAndJerry.Services
2+
@inject IApplicationService AppService
3+
@inherits BaseComponent
4+
5+
<div class="video-grid">
6+
@if (StateService.IsLoading)
7+
{
8+
<!-- Loading skeleton -->
9+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
10+
@for (int i = 0; i < 8; i++)
11+
{
12+
<div class="animate-pulse">
13+
<div class="bg-gray-200 rounded-lg aspect-video mb-3"></div>
14+
<div class="space-y-2">
15+
<div class="h-4 bg-gray-200 rounded w-3/4"></div>
16+
<div class="h-3 bg-gray-200 rounded w-1/2"></div>
17+
</div>
18+
</div>
19+
}
20+
</div>
21+
}
22+
else if (StateService.CurrentVideos.Any())
23+
{
24+
<!-- Video grid -->
25+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
26+
@foreach (var video in StateService.CurrentVideos)
27+
{
28+
<Thumbnail VideoModel="@video" />
29+
}
30+
</div>
31+
}
32+
else
33+
{
34+
<!-- Empty state -->
35+
<div class="text-center py-12">
36+
<div class="text-gray-500">
37+
<svg class="w-16 h-16 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
38+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
39+
</svg>
40+
<h3 class="text-lg font-medium text-gray-900 mb-2">No videos available</h3>
41+
<p class="text-gray-500">Check back later for new episodes!</p>
42+
</div>
43+
</div>
44+
}
45+
</div>
46+
47+
@code {
48+
protected override async Task OnComponentInitializedAsync()
49+
{
50+
if (!AppService.IsInitialized)
51+
{
52+
await AppService.InitializeApplicationAsync();
53+
}
54+
}
55+
}

DataBase/Data.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace TomAndJerry.DataBase;
44

55
public class Data
66
{
7-
public event Action OnChange;
7+
public event Action? OnChange;
88

99
private void NotifyDataChanged()
1010
{

Model/Video.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
public class Video
44
{
5-
public string Id { get; set; }
5+
public string Id { get; set; } = string.Empty;
66
public string Thumbnail { get; set; } = string.Empty;
77
public string CommentName { get; set; } = string.Empty;
88

0 commit comments

Comments
 (0)