Browse Source

feat(Navigation) Added a search button for desktop users

main
Jonathan McCaffrey 4 years ago
parent
commit
b9e3633d1c
  1. 2
      Components/Form/FormTextComponent.razor
  2. 61
      Components/Inputs/SearchButtonComponent.razor
  3. 42
      Components/Navigation/DesktopNavComponent.razor
  4. 1
      Components/_Imports.razor
  5. 3
      IGP/App.razor
  6. BIN
      IGP/Database.db
  7. 172
      IGP/Dialog/SearchDialogComponent.razor
  8. 4
      IGP/Index.razor
  9. 37
      IGP/PageLayout.razor
  10. 2
      IGP/Pages/Database/DatabaseSinglePage.razor
  11. 1
      IGP/Pages/RawDatabase.razor
  12. 28
      IGP/Portals/SearchPortal.razor
  13. 1
      IGP/Program.cs
  14. 13
      IGP/wwwroot/content/notes/settings/hotkeys.md
  15. 2
      IGP/wwwroot/generated/AgileTaskModels.json
  16. 2
      IGP/wwwroot/generated/NoteContentModels.json
  17. 2
      IGP/wwwroot/generated/WebSectionModels.json
  18. 11
      IGP/wwwroot/index.html
  19. 35
      Model/Notes/NoteContentModel.cs
  20. 9
      Model/Website/SearchPointModel.cs
  21. 1
      Model/Website/WebSectionModel.cs
  22. 5
      Services/Development/NoteService.cs
  23. 20
      Services/IServices.cs
  24. 131
      Services/Website/SearchService.cs

2
Components/Form/FormTextComponent.razor

@ -12,6 +12,7 @@
type="text" type="text"
value="@Value" value="@Value"
id="@labelId" id="@labelId"
@oninput="OnChange"
@onchange="OnChange"/> @onchange="OnChange"/>
</div> </div>
@if (Info != "") @if (Info != "")
@ -62,6 +63,7 @@
[Parameter] [Parameter]
public EventCallback<ChangeEventArgs> OnChange { get; set; } public EventCallback<ChangeEventArgs> OnChange { get; set; }
[Parameter] [Parameter]
public bool ReadOnly { get; set; } public bool ReadOnly { get; set; }

61
Components/Inputs/SearchButtonComponent.razor

@ -0,0 +1,61 @@
@using System.Runtime.InteropServices
@inject ISearchService searchService
@inject NavigationManager navigationManager
@inject IJSRuntime jsRuntime
<button class="searchButtonContainer" @onclick="ButtonClicked">
<div class="searchText">
Search...
</div>
@if (false)
{
<div class="searchHotkey">
@CommandKey + K
</div>
}
</button>
<style>
.searchButtonContainer {
background-color: var(--primary);
border: 2px solid var(--primary-border);
border-radius: 8px;
font-weight: 800;
width: 350px;
padding: 5px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.searchHotkey {
padding: 2px;
border: 2px solid var(--primary-border);
}
</style>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
private string userAgent = "";
string CommandKey => userAgent.Contains("Mac OS") ? "CMD" : "Ctrl";
private void ButtonClicked(EventArgs eventArgs)
{
searchService.Show();
}
protected override async Task OnInitializedAsync()
{
userAgent = await jsRuntime.InvokeAsync<string>("getUserAgent");
}
}

42
Components/Navigation/DesktopNavComponent.razor

@ -16,6 +16,7 @@
IGP Fan Reference IGP Fan Reference
</NavLink> </NavLink>
<div class="sectionNavs">
@foreach (var webSection in WebSections) @foreach (var webSection in WebSections)
{ {
var isSelected = navigationService.GetNavigationSectionId().Equals(webSection.Id); var isSelected = navigationService.GetNavigationSectionId().Equals(webSection.Id);
@ -25,7 +26,7 @@
sectionButtonStyle += " sectionButtonSelected"; sectionButtonStyle += " sectionButtonSelected";
} }
<div> <div class="sectionNav">
<button onclick="@(() => { MenuClicked(webSection.Id); })" class="@sectionButtonStyle">@webSection.Name</button> <button onclick="@(() => { MenuClicked(webSection.Id); })" class="@sectionButtonStyle">@webSection.Name</button>
@if (isSelected) @if (isSelected)
@ -39,6 +40,9 @@
</div> </div>
} }
</div> </div>
<SearchButtonComponent />
</div>
</div> </div>
<style> <style>
@ -57,10 +61,7 @@
.sectionButton { .sectionButton {
cursor: pointer; cursor: pointer;
padding-left: 12px; padding: 12px;
padding-right: 12px;
padding-top: 10px;
top: -11px;
position: relative; position: relative;
z-index: 50000; z-index: 50000;
} }
@ -78,14 +79,32 @@
border-bottom: 4px solid black; border-bottom: 4px solid black;
position: fixed; position: fixed;
width: 100%; width: 100%;
padding: 12px; padding-top: 12px;
padding-bottom: 12px;
padding-left: 24px;
padding-right: 24px;
display: flex; display: flex;
justify-content: center;
gap: 8px;
height: 50px;
background-color: var(--accent); background-color: var(--accent);
justify-content: space-between;
align-items: center;
}
.sectionNavs {
display: flex;
gap: 8px;
align-items: center;
} }
.sectionNav {
display: flex;
align-items: center;
height: 100%;
}
.websiteTitle { .websiteTitle {
font-weight: bold; font-weight: bold;
@ -95,8 +114,8 @@
.navMenuPosition { .navMenuPosition {
position: relative; position: relative;
top: calc(100% - 36px); top: 18px;
left: calc(50% + -330px / 2); left: calc(-50% + -330px / 2);
width: 0; width: 0;
height: 0; height: 0;
} }
@ -119,7 +138,6 @@
} }
.sectionButtonSelected { .sectionButtonSelected {
padding-bottom: 14px;
box-shadow: 0 2px 3px black; box-shadow: 0 2px 3px black;
background-color: var(--info); background-color: var(--info);
transform: translateY(-1px) scale(1.08); transform: translateY(-1px) scale(1.08);

1
Components/_Imports.razor

@ -16,3 +16,4 @@
@using System.Timers; @using System.Timers;
@using System; @using System;
@using YamlDotNet.Serialization @using YamlDotNet.Serialization
@using Components.Inputs;

3
IGP/App.razor

@ -1,4 +1,4 @@
@inject HttpClient HttpClient @inject HttpClient httpClient
<Router AppAssembly="@typeof(App).Assembly"> <Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData"> <Found Context="routeData">
@ -15,6 +15,7 @@
<EntityDialogPortal/> <EntityDialogPortal/>
<ToastPortal/> <ToastPortal/>
<SearchPortal />
<style> <style>
a { a {

BIN
IGP/Database.db

Binary file not shown.

172
IGP/Dialog/SearchDialogComponent.razor

@ -0,0 +1,172 @@
@implements IDisposable;
@inject ISearchService searchService
@inject IJSRuntime jsRuntime
@inject NavigationManager navigationManager
@if (searchService.IsLoaded())
{
<div class="searchBackground" onclick="@CloseDialog">
<div class="searchContainer"
@onclick:preventDefault="true"
@onclick:stopPropagation="true">
<FormLayoutComponent>
<FormTextComponent Placeholder="Search..." OnChange="SearchChanged"></FormTextComponent>
</FormLayoutComponent>
<div class="searchBox">
@if (SearchText.Length > 0)
{
foreach (var searchSection in searchService.Searches)
{
var searchPoints = searchSection.Value.FindAll(x => x.Title.ToLower().Contains(SearchText.ToLower()));
@if (searchPoints.Count > 0)
{
<div>
<div class="searchSectionTitle">
@searchSection.Key
</div>
<div class="searchContents">
@foreach (var searchPoint in searchPoints)
{
<button class="searchLink @searchPoint.PointType.ToLower()" @onclick="() => OnSearch(searchPoint)">@searchPoint.Title</button>
}
</div>
</div>
}
}
}
</div>
</div>
</div>
}
<style>
.pageContents * {
filter: blur(2px);
}
.searchBackground {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
}
.searchBox {
padding: 12px;
overflow-y: scroll;
overflow-x: hidden;
height: 530px;
border: 1px solid black;
border-radius: 2px;
}
.searchContents {
display: flex;
flex-direction: column;
gap: 6px;
align-items: flex-start;
padding: 12px;
}
.searchSectionTitle {
font-weight: bolder;
}
.searchContainer {
margin-left: auto;
margin-right: auto;
margin-top: 64px;
width: 800px;
height: 600px;
background-color: var(--background);
border-width: var(--dialog-border-width);
border-style: solid;
border-color: var(--dialog-border-color);
border-radius: var(--dialog-radius);
padding: 8px;
box-shadow: 1px 2px 2px black;
}
.searchLink {
text-decoration: underline;
}
</style>
@code {
private ElementReference searchBox;
private string SearchText { get; set; } = "";
protected override void OnInitialized()
{
searchService.Subscribe(OnSearchChanged);
}
private void OnSearchChanged()
{
if (searchService.IsVisible)
{
jsRuntime.InvokeVoidAsync("SetFocusToElement", "search-dialog-input");
}
}
public void Dispose()
{
searchService.Unsubscribe(OnSearchChanged);
}
public void CloseDialog()
{
searchService.Hide();
}
public void NavigateTo(string url)
{
if (url.Contains("#"))
{
navigationManager.NavigateTo(url,
navigationManager.Uri.Split("#").First().Contains(url.Split("#").First()));
}
else
{
navigationManager.NavigateTo(url);
}
}
private void SearchChanged(ChangeEventArgs obj)
{
SearchText = obj.Value!.ToString()!;
}
private void OnSearch(SearchPointModel searchPoint)
{
NavigateTo(searchPoint.Href);
searchService.Hide();
}
}

4
IGP/Index.razor

@ -2,4 +2,8 @@
@layout PageLayout @layout PageLayout
<DevOnlyComponent>
<SearchButtonComponent />
</DevOnlyComponent>
<HomePage/> <HomePage/>

37
IGP/PageLayout.razor

@ -1,8 +1,11 @@
@inherits LayoutComponentBase; @inherits LayoutComponentBase;
@inject IJSRuntime jsRuntime
@inject ISearchService searchService
@inject IWebsiteService webService; @inject IWebsiteService webService;
@implements IDisposable; @implements IDisposable;
<div class="pageContents">
<div class="layoutContainer"> <div class="layoutContainer">
@if (!webService.IsLoaded()) @if (!webService.IsLoaded())
{ {
@ -24,15 +27,9 @@
} }
</div> </div>
@code { </div>
#if NO_SQL
#else
[Inject]
DatabaseContext Database { get; set; }
#endif
@code {
protected override void OnInitialized() protected override void OnInitialized()
{ {
@ -42,6 +39,19 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await webService.Load(); await webService.Load();
await Focus();
}
private async Task Focus()
{
// await jsRuntime.InvokeVoidAsync("SetFocusToElement", pageContents);
}
protected override async void OnAfterRender(bool firstRender)
{
// await jsRuntime.InvokeVoidAsync("SetFocusToElement", pageContents);
} }
void IDisposable.Dispose() void IDisposable.Dispose()
@ -54,4 +64,15 @@
StateHasChanged(); StateHasChanged();
} }
private void HandleKeyDown(KeyboardEventArgs keyboardEventArgs)
{
if ((keyboardEventArgs.CtrlKey || keyboardEventArgs.MetaKey) && keyboardEventArgs.Key.ToLower() == "k")
{
searchService.Show();
}
Console.WriteLine(keyboardEventArgs.CtrlKey + " " + keyboardEventArgs.Key);
}
} }

2
IGP/Pages/Database/DatabaseSinglePage.razor

@ -69,7 +69,7 @@
foreach (var e in DATA.Get().Values) foreach (var e in DATA.Get().Values)
{ {
if (e.Info().Name.Equals(Text)) if (e.Info().Name.ToLower().Equals(Text!.ToLower()))
{ {
entity = e; entity = e;
return; return;

1
IGP/Pages/RawDatabase.razor

@ -5,7 +5,6 @@
<Title>Placeholders and Speculative</Title> <Title>Placeholders and Speculative</Title>
<Message>The data I am using contains placeholders and speculation on future mechanics. Ignore said data when using this JSON.</Message> <Message>The data I am using contains placeholders and speculation on future mechanics. Ignore said data when using this JSON.</Message>
</AlertComponent> </AlertComponent>
<CodeComponent> <CodeComponent>
@DATA.AsJson() @DATA.AsJson()
</CodeComponent> </CodeComponent>

28
IGP/Portals/SearchPortal.razor

@ -0,0 +1,28 @@
@implements IDisposable;
@inject ISearchService searchService
@if (searchService.IsVisible)
{
<SearchDialogComponent></SearchDialogComponent>
}
@code {
protected override void OnInitialized()
{
searchService.Subscribe(OnUpdate);
searchService.Load();
}
public void Dispose()
{
searchService.Unsubscribe(OnUpdate);
}
void OnUpdate()
{
StateHasChanged();
}
}

1
IGP/Program.cs

@ -36,6 +36,7 @@ builder.Services.AddSingleton<IAgileService, AgileService>();
builder.Services.AddSingleton<IGitService, GitService>(); builder.Services.AddSingleton<IGitService, GitService>();
builder.Services.AddSingleton<INoteService, NoteService>(); builder.Services.AddSingleton<INoteService, NoteService>();
builder.Services.AddSingleton<IDocumentationService, DocumentationService>(); builder.Services.AddSingleton<IDocumentationService, DocumentationService>();
builder.Services.AddSingleton<ISearchService, SearchService>();
builder.Services.AddSingleton(new HttpClient builder.Services.AddSingleton(new HttpClient
{ {

13
IGP/wwwroot/content/notes/settings/hotkeys.md

@ -9,7 +9,7 @@ In the pre-alpha, IGP comes with some Unreal default hotkey setup.
This document will explain how to set up, modify, and use a new hotkey setup. This document will explain how to set up, modify, and use a new hotkey setup.
## Save the "Input.ini" file # Save the "Input.ini" file
***Copy the below content and save the file as `Input.ini`.*** ***Copy the below content and save the file as `Input.ini`.***
@ -90,15 +90,14 @@ ActionMappings = (ActionName="UnitTypeSelectionModifier",bShift=False,bCtrl=Fals
***Copy the above content and save the file as `Input.ini`.*** ***Copy the above content and save the file as `Input.ini`.***
## Understand the Input.ini # Understand the Input.ini
You can notice a single line of this file can be broken down like this. You can notice a single line of this file can be broken down like this.
`ActionMappings=(ActionName="AttackMove",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=A)` `ActionMappings=(ActionName="AttackMove",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=A)`
- `ActionMappings=(***)`: Indicates content is an action mapping. i.e. a hotkey - `ActionMappings=(***)`: Indicates content is an action mapping. i.e. a hotkey
- `ActionName="AttackMove"`: Indicates the name of the selected action. Here, we are using the `AttackMove` move action, - `ActionName="AttackMove"`: Indicates the name of the selected action. Here, we are using the `AttackMove` move action, that forces your selected army to attack.
that forces your selected army to attack.
- `Key=A`: Indicates key being mapped to the action. Set to `Key=Tab` to require the Tab key to be pressed instead, to - `Key=A`: Indicates key being mapped to the action. Set to `Key=Tab` to require the Tab key to be pressed instead, to
perform the `AttackMove` action. perform the `AttackMove` action.
- `bShift=False`: Indicates that the Shift key is not held. Set to `bShift=True` to require a shift key held to - `bShift=False`: Indicates that the Shift key is not held. Set to `bShift=True` to require a shift key held to
@ -110,7 +109,7 @@ You can notice a single line of this file can be broken down like this.
- `bCmd=False`: Indicates that the Cmd key is not held. Set to `bCmd=True` to require a cmd key held to perform the - `bCmd=False`: Indicates that the Cmd key is not held. Set to `bCmd=True` to require a cmd key held to perform the
mapped action. (Macs not supported by IGP) mapped action. (Macs not supported by IGP)
## Modify the Input.ini file # Modify the Input.ini file
You are now going to want to modify the file with your own hotkey setup. You are now going to want to modify the file with your own hotkey setup.
@ -258,10 +257,10 @@ to use proper values for `YOUR_USER` and `CURRENT_IMMORTAL_CLIENT`.
There will be a blank `Input.ini` file in this folder. You can safely override it with your modified file. There will be a blank `Input.ini` file in this folder. You can safely override it with your modified file.
## Testing # Testing
Restart the IGP client, and try out your new hotkey setup! Restart the IGP client, and try out your new hotkey setup!
## Trouble Shooting # Trouble Shooting
If running into trouble, ask for help in the `hotkeys` channel in the IGP Discord. If running into trouble, ask for help in the `hotkeys` channel in the IGP Discord.

2
IGP/wwwroot/generated/AgileTaskModels.json

File diff suppressed because one or more lines are too long

2
IGP/wwwroot/generated/NoteContentModels.json

@ -1 +1 @@
[{"Id":1,"ParentId":null,"NoteSectionModelId":3,"Href":"holdout","CreatedDate":"2022-02-18T00:00:00","UpdatedDate":"2022-02-18T00:00:00","Name":"Coop Holdout, Some distant place (Nuath)","Description":"First coop test map in pre-alpha.","Content":"coop/holdout","IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":2,"ParentId":null,"NoteSectionModelId":2,"Href":"hotkeys","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Custom HotKey Setup","Description":"Customize your hotkeys in the pre-alpha.","Content":"settings/hotkeys","IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":3,"ParentId":null,"NoteSectionModelId":1,"Href":"armor-types","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Armor Types","Description":"Heavy, Medium, and Light. What does it mean?","Content":"the-basics/armor-types","IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0}] [{"Id":1,"ParentId":null,"NoteSectionModelId":3,"Href":"holdout","CreatedDate":"2022-02-18T00:00:00","UpdatedDate":"2022-02-18T00:00:00","Name":"Coop Holdout, Some distant place (Nuath)","Description":"First coop test map in pre-alpha.","Content":"coop/holdout","LoadedContent":null,"IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":2,"ParentId":null,"NoteSectionModelId":2,"Href":"hotkeys","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Custom HotKey Setup","Description":"Customize your hotkeys in the pre-alpha.","Content":"settings/hotkeys","LoadedContent":null,"IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0},{"Id":3,"ParentId":null,"NoteSectionModelId":1,"Href":"armor-types","CreatedDate":"2022-04-13T00:00:00","UpdatedDate":"2022-04-13T00:00:00","Name":"Armor Types","Description":"Heavy, Medium, and Light. What does it mean?","Content":"the-basics/armor-types","LoadedContent":null,"IsHidden":"False","IsPreAlpha":"True","NoteContentModels":[],"Parent":null,"PageOrder":0}]

2
IGP/wwwroot/generated/WebSectionModels.json

@ -1 +1 @@
[{"Id":1,"Name":"Tools","Description":"Tools Stuff","Href":null,"Order":1,"IsPrivate":"False","WebPageModels":[]},{"Id":2,"Name":"Resources","Description":"Resources Stuff","Href":null,"Order":2,"IsPrivate":"False","WebPageModels":[]},{"Id":3,"Name":"General","Description":"About Stuff","Href":null,"Order":3,"IsPrivate":"False","WebPageModels":[]},{"Id":4,"Name":"Development","Description":"Development Stuff","Href":null,"Order":4,"IsPrivate":"False","WebPageModels":[]},{"Id":5,"Name":"Settings","Description":"Settings Stuff","Href":null,"Order":5,"IsPrivate":"False","WebPageModels":[]}] [{"Id":1,"Name":"Tools","Description":"Tools Stuff","Order":1,"IsPrivate":"False","WebPageModels":[]},{"Id":2,"Name":"Resources","Description":"Resources Stuff","Order":2,"IsPrivate":"False","WebPageModels":[]},{"Id":3,"Name":"General","Description":"About Stuff","Order":3,"IsPrivate":"False","WebPageModels":[]},{"Id":4,"Name":"Development","Description":"Development Stuff","Order":4,"IsPrivate":"False","WebPageModels":[]},{"Id":5,"Name":"Settings","Description":"Settings Stuff","Order":5,"IsPrivate":"False","WebPageModels":[]}]

11
IGP/wwwroot/index.html

@ -36,6 +36,17 @@
applicationCulture: 'en-US' applicationCulture: 'en-US'
}); });
</script> </script>
<script>
window.getUserAgent = () => {
return navigator.userAgent;
};
</script>
<script>
window.SetFocusToElement = (elementId) => {
document.getElementById(elementId).focus();
};
</script>
<script>navigator.serviceWorker.register('service-worker.js');</script> <script>navigator.serviceWorker.register('service-worker.js');</script>
</body> </body>

35
Model/Notes/NoteContentModel.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Text.RegularExpressions;
using Model.Website;
namespace Model.Notes; namespace Model.Notes;
@ -22,9 +24,42 @@ public class NoteContentModel
public string Description { get; set; } public string Description { get; set; }
public string Content { get; set; } public string Content { get; set; }
[NotMapped]
public virtual string LoadedContent { get; set; }
public string IsHidden { get; set; } = "False"; public string IsHidden { get; set; } = "False";
public string IsPreAlpha { get; set; } = "True"; public string IsPreAlpha { get; set; } = "True";
public List<SearchPointModel> GetHeaders()
{
var regex = new Regex(@"^#* (.*)$", RegexOptions.Multiline);
var listOfMatches = regex.Matches(LoadedContent);
Console.WriteLine($"Name: {Name}");
Console.WriteLine($"Matches: {listOfMatches.Count}");
List<SearchPointModel> foundHeaders = new List<SearchPointModel>();
foreach (var capture in listOfMatches)
{
var cleanUp = capture.ToString();
cleanUp = cleanUp.ToLower();
cleanUp = cleanUp.Replace("#", "");
cleanUp = cleanUp.Replace("#", "");
cleanUp = cleanUp.Replace("\"", "");
cleanUp = cleanUp.Trim();
cleanUp = cleanUp.Replace(" ", "-");
foundHeaders.Add(new SearchPointModel(){ Title = capture.ToString().Trim(), Href = cleanUp});
Console.WriteLine($"Capture: {cleanUp}");
}
return foundHeaders;
}
[NotMapped] [NotMapped]
public virtual ICollection<NoteContentModel> NoteContentModels { get; set; } = new List<NoteContentModel>(); public virtual ICollection<NoteContentModel> NoteContentModels { get; set; } = new List<NoteContentModel>();

9
Model/Website/SearchPointModel.cs

@ -0,0 +1,9 @@
namespace Model.Website;
public class SearchPointModel
{
public string Title { get; set; } = "";
public string Tags { get; set; } = "";
public string PointType { get; set; } = "";
public string Href { get; set; } = "";
}

1
Model/Website/WebSectionModel.cs

@ -8,7 +8,6 @@ public class WebSectionModel
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } = "Add name"; public string Name { get; set; } = "Add name";
public string Description { get; set; } = "Add description"; public string Description { get; set; } = "Add description";
public string Href { get; set; } = null;
public int Order { get; set; } = 0; public int Order { get; set; } = 0;
public string IsPrivate { get; set; } = "True"; public string IsPrivate { get; set; } = "True";

5
Services/Development/NoteService.cs

@ -59,6 +59,11 @@ public class NoteService : INoteService
isLoaded = true; isLoaded = true;
foreach (var content in NoteContentModels)
{
content.LoadedContent = await httpClient.GetStringAsync($"content/notes/{content.Content}.md");
}
SortSQL(); SortSQL();
NotifyDataChanged(); NotifyDataChanged();

20
Services/IServices.cs

@ -25,6 +25,26 @@ public interface IToastService
void ClearAllToasts(); void ClearAllToasts();
} }
public interface ISearchService
{
public List<SearchPointModel> SearchPoints { get; set; }
public Dictionary<string, List<SearchPointModel>> Searches { get; set; }
public bool IsVisible { get; set; }
public void Subscribe(Action action);
public void Unsubscribe(Action action);
public void Search(string entityId);
public Task Load();
public bool IsLoaded();
void Show();
void Hide();
}
public interface IEntityDialogService public interface IEntityDialogService
{ {
public void Subscribe(Action action); public void Subscribe(Action action);

131
Services/Website/SearchService.cs

@ -0,0 +1,131 @@
using Model.Entity.Data;
using Model.Feedback;
using Model.Website;
namespace Services.Website;
public class SearchService : ISearchService
{
private bool isLoaded = false;
public List<SearchPointModel> SearchPoints { get; set; } = new();
public Dictionary<string, List<SearchPointModel>> Searches { get; set; } = new();
private IWebsiteService websiteService;
private INoteService noteService;
private IDocumentationService documentationService;
public SearchService(IWebsiteService websiteService, INoteService noteService, IDocumentationService documentationService)
{
this.websiteService = websiteService;
this.noteService = noteService;
this.documentationService = documentationService;
// All Unit Data
}
public bool IsVisible { get; set; }
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action)
{
OnChange += action;
}
public void Search(string entityId)
{
}
public async Task Load()
{
await websiteService.Load();
await noteService.Load();
await documentationService.Load();
Searches.Add("Pages", new List<SearchPointModel>());
Searches.Add("Notes", new List<SearchPointModel>());
Searches.Add("Documents", new List<SearchPointModel>());
Searches.Add("Entities", new List<SearchPointModel>());
foreach (var webPage in websiteService.WebPageModels)
{
SearchPoints.Add(new SearchPointModel{ Title = webPage.Name,
PointType = "WebPage",
Href=webPage.Href
});
Searches["Pages"].Add(SearchPoints.Last());
}
foreach (var note in noteService.NoteContentModels)
{
SearchPoints.Add(new SearchPointModel(){ Title = note.Name,
PointType = "Note", Href = note.GetNoteLink()});
Searches["Notes"].Add(SearchPoints.Last());
foreach (var noteHeader in note.GetHeaders())
{
SearchPoints.Add(new SearchPointModel { Title = noteHeader.Title,
PointType = "NoteHeader", Href = $"{note.GetNoteLink()}/#{noteHeader.Href}"});
}
}
foreach (var entity in DATA.Get().Values)
{
SearchPoints.Add(new SearchPointModel(){
Title = entity.Info().Name,
Tags = $"{entity.EntityType},{entity.Descriptive}",
PointType = "Entity",
Href = $"database/{entity.Info().Name.ToLower()}"
});
Searches["Entities"].Add(SearchPoints.Last());
}
foreach (var doc in documentationService.DocContentModels)
{
SearchPoints.Add(new SearchPointModel { Title = doc.Name,
PointType = "Document", Href = doc.GetDocLink()});
Searches["Documents"].Add(SearchPoints.Last());
}
isLoaded = true;
}
public bool IsLoaded()
{
return isLoaded;
}
public void Show()
{
IsVisible = true;
NotifyDataChanged();
}
public void Hide()
{
IsVisible = false;
NotifyDataChanged();
}
private event Action OnChange = null!;
private void NotifyDataChanged()
{
OnChange();
}
}
Loading…
Cancel
Save