add history of searches in board

This commit is contained in:
marlon-erler
2024-07-27 03:41:47 +02:00
parent e9425b95fa
commit 845a9d2f35
6 changed files with 165 additions and 87 deletions
+1 -3
View File
@@ -115,11 +115,9 @@ export function BoardPage(boardViewModel: BoardViewModel) {
{BoardSettingsModal(boardViewModel)}
{SearchModal(
boardViewModel.searchViewModel,
translations.chatPage.task.filterTasksHeadline,
boardViewModel.taskViewModels,
boardViewModel.filteredTaskViewModels,
TaskViewModelToEntry,
TaskViewModel.getStringsForFilter,
boardViewModel.isPresentingFilterModal
)}
<div children:set={taskSettingsModal}></div>
+16 -14
View File
@@ -1,26 +1,22 @@
import * as React from "bloatless-react";
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
import { StringToOption } from "../Components/option";
import { translations } from "../translations";
import { v4 } from "uuid";
export function SearchModal<T>(
searchViewModel: SearchViewModel<T>,
headline: string,
allObjects: React.ListState<T> | React.MapState<T>,
filteredObjects: React.ListState<T>,
converter: React.StateItemConverter<T>,
getStringsOfObject: (object: T) => string[],
isOpen: React.State<boolean>
) {
const viewModel: SearchViewModel<T> = new SearchViewModel(
allObjects,
filteredObjects,
getStringsOfObject
);
function close() {
isOpen.value = false;
}
const id = v4();
return (
<div class="modal" toggle:open={isOpen}>
<div style="max-width: 64rem">
@@ -29,14 +25,20 @@ export function SearchModal<T>(
<div class="flex-row width-input">
<input
placeholder={translations.general.searchLabel}
bind:value={viewModel.searchInput}
on:enter={viewModel.applySearch}
bind:value={searchViewModel.searchInput}
on:enter={searchViewModel.applySearch}
list={id}
></input>
<datalist
hidden
id={id}
children:append={[searchViewModel.suggestions, StringToOption]}
></datalist>
<button
class="primary"
aria-label={translations.general.searchButtonAudioLabel}
on:click={viewModel.applySearch}
toggle:disabled={viewModel.cannotApplySearch}
on:click={searchViewModel.applySearch}
toggle:disabled={searchViewModel.cannotApplySearch}
>
<span class="icon">search</span>
</button>
@@ -47,7 +49,7 @@ export function SearchModal<T>(
<div
class="grid gap"
style="grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr))"
children:append={[filteredObjects, converter]}
children:append={[searchViewModel.matchingObjects, converter]}
></div>
</main>
<button on:click={close}>
+35 -1
View File
@@ -7,6 +7,7 @@ import BoardsAndTasksModel, {
import { Color } from "../../colors";
import CoreViewModel from "../Global/coreViewModel";
import SearchViewModel from "../Utility/searchViewModel";
import StorageModel from "../../Model/Global/storageModel";
import TaskContainingPageViewModel from "./taskContainingPageViewModel";
import TaskPageViewModel from "./taskPageViewModel";
@@ -35,8 +36,10 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
isPresentingSettingsModal: React.State<boolean> = new React.State(false);
isPresentingFilterModal: React.State<boolean> = new React.State(false);
searchViewModel: SearchViewModel<TaskViewModel>;
filteredTaskViewModels: React.ListState<TaskViewModel> =
new React.ListState();
searchSuggestions: React.ListState<string> = new React.ListState();
// paths
getBasePath = (): string[] => {
@@ -47,6 +50,10 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
return [...this.getBasePath(), BoardViewModelSubPath.LastUsedView];
};
getPreviousSearchesPath = (): string[] => {
return [...this.getBasePath(), BoardViewModelSubPath.PreviousSearches];
};
// settings
saveSettings = (): void => {
const newBoardInfoFileContent: BoardInfoFileContent =
@@ -70,7 +77,7 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
// methods
createTask = (): void => {
this.createTaskFromBoardId(this.boardInfo.fileId);
}
};
handleDropWithinBoard = (category?: string, status?: string): void => {
const draggedObject: any = this.coreViewModel.draggedObject.value;
@@ -99,6 +106,14 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
this.selectedPage.value = lastUsedView as BoardPageType;
};
storeSearchTerm = (searchTerm: string): void => {
const path: string[] = [...this.getPreviousSearchesPath(), searchTerm];
this.storageModel.write(path, "");
if (!this.searchSuggestions.value.has(searchTerm)) {
this.searchSuggestions.add(searchTerm);
}
};
// view
showTask = (taskFileContent: TaskFileContent): void => {
if (taskFileContent.boardId != this.boardInfo.fileId) {
@@ -186,9 +201,16 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
this.updateTaskIndices();
};
loadSearchSuggestions = (): void => {
const dirPath: string[] = this.getPreviousSearchesPath();
const searches: string[] = this.storageModel.list(dirPath);
this.searchSuggestions.add(...searches);
};
loadData = (): void => {
this.restoreLastUsedView();
this.loadTasks();
this.loadSearchSuggestions();
};
// init
@@ -234,11 +256,23 @@ export default class BoardViewModel extends TaskContainingPageViewModel {
this.updateTaskIndices();
}
);
// search
this.searchViewModel = new SearchViewModel(
this.taskViewModels,
this.filteredTaskViewModels,
TaskViewModel.getStringsForFilter,
this.searchSuggestions
);
this.searchViewModel.appliedQuery.subscribe((newQuery) => {
this.storeSearchTerm(newQuery);
});
}
}
export enum BoardViewModelSubPath {
LastUsedView = "last-used-view",
PreviousSearches = "previous-searches",
}
// types
+10 -1
View File
@@ -12,6 +12,8 @@ export default class SearchViewModel<T> {
searchInput: React.State<string> = new React.State("");
matchingObjects: React.ListState<T>;
suggestions: React.ListState<string>;
// guards
cannotApplySearch: React.State<boolean> = React.createProxyState(
[this.searchInput, this.appliedQuery],
@@ -19,6 +21,11 @@ export default class SearchViewModel<T> {
);
// methods
search = (searchTerm: string): void => {
this.searchInput.value = searchTerm;
this.applySearch();
};
applySearch = (): void => {
this.appliedQuery.value = this.searchInput.value;
console.log("applying search");
@@ -36,11 +43,13 @@ export default class SearchViewModel<T> {
constructor(
allObjects: React.ListState<T> | React.MapState<T>,
matchingObjects: React.ListState<T>,
getStringsOfObject: (object: T) => string[]
getStringsOfObject: (object: T) => string[],
suggestions: React.ListState<string>
) {
this.allObjects = allObjects;
this.matchingObjects = matchingObjects;
this.getStringsOfObject = getStringsOfObject;
this.suggestions = suggestions;
// handle new objects
this.allObjects.handleAddition((newObject: T) => {