1
+ @using FairPlayCombined .Common .CustomAttributes
2
+ @using FairPlayCombined .Interfaces
3
+ @using FairPlayCombined .Interfaces .Common
4
+ @using FairPlayCombined .Models .Common .VisitorTracking
5
+ @using Microsoft .Extensions .Localization
6
+ @using System .Timers
7
+
8
+ @implements IAsyncDisposable
9
+
10
+ @inherits LayoutComponentBase
11
+ @inject IStringLocalizer <MainLayout > localizer
12
+ @inject NavigationManager navigationManager
13
+ @inject IVisitorTrackingService visitorTrackingService
14
+ @inject IUserProviderService userProviderService
15
+ @inject IStringLocalizer <MainLayout > localizer
16
+
17
+ <FluentMainLayout NavMenuWidth =" 400" >
18
+ <Header >
19
+ <div class =" header-content" style =" display : flex ; align-items : center ; justify-content : center ; flex-wrap : wrap ;" >
20
+ <img src =" images/brand-logo.png" alt =" @nameof(FairPlayBlogs) Logo" class =" logo" style =" max-width : 100% ; height : auto ;" />
21
+ <FluentLabel Typo =" Typography.H3" Alignment =" HorizontalAlignment.Center" style =" display : flex ; align-items : center ; flex-wrap : nowrap ;" >
22
+ @nameof(FairPlayBlogs)
23
+ <FluentBadge Circular =" true"
24
+ Appearance =" Microsoft.FluentUI.AspNetCore.Components.Appearance.Neutral"
25
+ style =" margin-left : 8px ; white-space : nowrap ;" >
26
+ Beta
27
+ </FluentBadge >
28
+ </FluentLabel >
29
+ </div >
30
+ <div class =" search-controls" style =" display : flex ; align-items : center ; margin-top : 16px ;" >
31
+ <FluentTextField @bind-Value =" @this.SearchTerm" Placeholder =@SearchText style =" flex : 1 1 auto ;" >
32
+ </FluentTextField >
33
+ <FluentButton Type =" ButtonType.Button" OnClick =" OnSearchButtonClicked"
34
+ IconEnd =" @(new Icons.Regular.Size20.Search())" style =" margin-left : 8px ;" >
35
+ </FluentButton >
36
+ </div >
37
+ </Header >
38
+ <Body >
39
+ @Body
40
+ </Body >
41
+ <NavMenuContent >
42
+ <NavMenu ></NavMenu >
43
+ </NavMenuContent >
44
+ </FluentMainLayout >
45
+
46
+
47
+ <div id =" blazor-error-ui" >
48
+ An unhandled error has occurred.
49
+ <a href =" " class =" reload" >Reload</a >
50
+ <a class =" dismiss" >🗙</a >
51
+ </div >
52
+
53
+ <FluentToastProvider />
54
+ <FluentDialogProvider />
55
+ <FluentTooltipProvider />
56
+ <FluentMessageBarProvider />
57
+ <FluentDesignTheme StorageName =" theme" />
58
+
59
+ @code
60
+ {
61
+ private bool IsBusy { get ; set ; } = false ;
62
+ private System .Timers .Timer ? VisitsTimer { get ; set ; }
63
+ private readonly CancellationTokenSource cancellationTokenSource = new ();
64
+ private long ? VisitorTrackingId { get ; set ; }
65
+ [Parameter ]
66
+ [SupplyParameterFromQuery ]
67
+ public string ? SearchTerm { get ; set ; }
68
+
69
+ protected override async Task OnInitializedAsync ()
70
+ {
71
+ try
72
+ {
73
+ IsBusy = true ;
74
+ await TrackVisit (createNewSession : true );
75
+ navigationManager .LocationChanged += NavigationManager_LocationChanged ;
76
+ }
77
+ catch (Exception )
78
+ {
79
+ // Ignore
80
+ }
81
+ finally
82
+ {
83
+ IsBusy = false ;
84
+ }
85
+ }
86
+
87
+ private async Task TrackVisit (bool createNewSession )
88
+ {
89
+ // We do not want to track authentication flow pages visits
90
+ if (navigationManager .Uri .Contains (" /authentication/" ))
91
+ return ;
92
+ VisitorTrackingModel visitorTrackingModel = new ()
93
+ {
94
+ VisitedUrl = navigationManager .Uri
95
+ };
96
+ var currentUserId = userProviderService .GetCurrentUserId ();
97
+ if (! String .IsNullOrWhiteSpace (currentUserId ))
98
+ {
99
+ visitorTrackingModel .ApplicationUserId = currentUserId ;
100
+ }
101
+ if (createNewSession )
102
+ {
103
+ visitorTrackingModel .SessionId = Guid .NewGuid ();
104
+ }
105
+ this .VisitorTrackingId = await this .visitorTrackingService
106
+ .TrackVisitAsync (visitorTrackingModel , this .cancellationTokenSource .Token );
107
+
108
+ if (createNewSession )
109
+ {
110
+ this .VisitsTimer = new System .Timers .Timer (TimeSpan .FromSeconds (60 ).TotalMilliseconds );
111
+ this .VisitsTimer .Elapsed += VisitsTimer_Elapsed ;
112
+ this .VisitsTimer .Start ();
113
+ }
114
+ }
115
+
116
+ private async void VisitsTimer_Elapsed (object ? sender , ElapsedEventArgs e )
117
+ {
118
+ try
119
+ {
120
+ await visitorTrackingService .UpdateVisitTimeElapsedAsync (this .VisitorTrackingId ! .Value ,
121
+ this .cancellationTokenSource .Token );
122
+ }
123
+ catch (Exception )
124
+ {
125
+ // Ignore
126
+ }
127
+ }
128
+
129
+ private async void NavigationManager_LocationChanged (object ? sender ,
130
+ Microsoft .AspNetCore .Components .Routing .LocationChangedEventArgs e )
131
+ {
132
+ try
133
+ {
134
+ await TrackVisit (createNewSession : false );
135
+ }
136
+ catch (Exception )
137
+ {
138
+ // Ignore
139
+ }
140
+ }
141
+
142
+ private void OnSearchButtonClicked ()
143
+ {
144
+ if (! String .IsNullOrWhiteSpace (this .SearchTerm ))
145
+ {
146
+ this .navigationManager .NavigateTo ($" /?SearchTerm={this .SearchTerm }" ,
147
+ forceLoad : true );
148
+ }
149
+ else
150
+ {
151
+ this .navigationManager .NavigateTo ($" /" , forceLoad : true );
152
+ }
153
+ }
154
+
155
+ public async ValueTask DisposeAsync ()
156
+ {
157
+ await this .cancellationTokenSource .CancelAsync ();
158
+ this .cancellationTokenSource .Dispose ();
159
+ }
160
+
161
+ #region Resource Keys
162
+ [ResourceKey (defaultValue : " Search" )]
163
+ public const string SearchTextKey = " SearchText" ;
164
+ public string SearchText => localizer [SearchTextKey ];
165
+ #endregion
166
+ }
0 commit comments