Skip to content

Commit

Permalink
store custome filters in localstorage and restore on load
Browse files Browse the repository at this point in the history
Signed-off-by: GRBurst <[email protected]>
  • Loading branch information
GRBurst committed May 24, 2024
1 parent 2627221 commit 0c7d0b6
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 37 deletions.
17 changes: 9 additions & 8 deletions frontend/src/components/CustomFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import { replaceTagCaptureGroup } from '../utils/hn'
interface CustomTagFilterProps {
onTagAdd: (key: string, tag: TagFilter) => void
}
const CustomTagFilter = ({onTagAdd}: CustomTagFilterProps) => {
const CustomTagFilter = ({ onTagAdd }: CustomTagFilterProps) => {
const [tagName, setTagName] = useState<string>("")
const [tagPattern, setTagPattern] = useState<string>("")
const [tagPatternFlags, setTagPatternFlags] = useState<string>("")

const addNewTag = () => {
if(tagName !== undefined && tagName != "") {
const newTag = (tagPattern !== undefined && tagPattern != "") ? TagFilter({name: tagName, pattern: RegExp(tagPattern, tagPatternFlags)}) : TagFilterSimple(tagName)
if (tagName !== undefined && tagName != "") {
const newFlags = (tagPatternFlags !== undefined && tagPatternFlags != "") ? tagPattern : "gmi"
const newTag = (tagPattern !== undefined && tagPattern != "") ? TagFilter({ name: tagName, pattern: RegExp(tagPattern, newFlags) }) : TagFilterSimple(tagName)
onTagAdd("Custom", replaceTagCaptureGroup(newTag))
setTagName("")
setTagPattern("")
Expand Down Expand Up @@ -56,10 +57,10 @@ const CustomTagFilter = ({onTagAdd}: CustomTagFilterProps) => {
interface SearchFilterProps {
onTextSearch: (needle: string | undefined) => void
}
const SearchFilter = ({onTextSearch}: SearchFilterProps) => {
const SearchFilter = ({ onTextSearch }: SearchFilterProps) => {
const { Search } = Input;
const onSearchInput: SearchProps['onSearch'] = (value) => {
if(value.length >= 3) {
const onSearchInput: SearchProps['onSearch'] = (value) => {
if (value.length >= 3) {
onTextSearch(value)
} else if (value.length == 0) {
onTextSearch(undefined)
Expand All @@ -71,7 +72,7 @@ const SearchFilter = ({onTextSearch}: SearchFilterProps) => {
placeholder="input search text"
allowClear
onSearch={onSearchInput}
onChange={(e) => {if(e.target.value.length >= 3) { onTextSearch(e.target.value)}}}
onChange={(e) => { if (e.target.value.length >= 3) { onTextSearch(e.target.value) } }}
enterButton
/>
)
Expand All @@ -81,7 +82,7 @@ interface CustomFiltersProps {
onTagAdd: (key: string, tag: TagFilter) => void
onSearch: (needle: string | undefined) => void
}
const CustomFilters = ({onTagAdd, onSearch}: CustomFiltersProps) => {
const CustomFilters = ({ onTagAdd, onSearch }: CustomFiltersProps) => {

return (
<Flex gap="middle" vertical>
Expand Down
16 changes: 11 additions & 5 deletions frontend/src/components/FilterableJobList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState } from "react";
import sanitizeHtml from "sanitize-html";

import { Item } from "../models/Item";
import { TagFilter, TagFilters } from "../models/TagFilter";
import { TagFilter, TagFilters, tagFilterToString } from "../models/TagFilter";
import { filterByRegexAny, flatFilters, itemFilter } from "../utils/hn";
import { TagFilterBar } from "./TagFilterBar";

Expand Down Expand Up @@ -61,9 +61,9 @@ const ItemList = ({ items, tagFilters, searchFilter }: ItemListProps) => (
sanitizeHtml(item.text ?? ""),
searchFilter !== undefined
? [
...tagFilters,
TagFilter({ name: "_Search_", pattern: RegExp(searchFilter) }),
]
...tagFilters,
TagFilter({ name: "_Search_", pattern: RegExp(searchFilter) }),
]
: tagFilters
);
return (
Expand Down Expand Up @@ -181,8 +181,14 @@ const FilterableJobList = ({
onInactive={(key: string, tag: TagFilter) =>
removeFilters(key, tag, activeTagFilters, setActiveTagFilters)
}
onTagAdd={(key: string, tag: TagFilter) =>
onTagAdd={(key: string, tag: TagFilter) => {
addFilters(key, tag, allTagFilters, setAllTagFilters)
//TODO: put name at central place
localStorage.setItem(
"CustomFilters",
JSON.stringify(Array.from(allTagFilters.get("Custom") ?? []).map(f => tagFilterToString(f)))
)
}
}
onSearch={(needle: string | undefined) => setSearchFilter(needle)}
/>
Expand Down
12 changes: 11 additions & 1 deletion frontend/src/components/HnJobs.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { HashSet as HSet } from "effect";
import type { HashSet } from "effect/HashSet";

import { lazy, useEffect, useState } from "react";
Expand All @@ -11,7 +12,7 @@ import {
import { App, ConfigProvider, theme } from "antd";

import { GithubIcon } from "./Icons";
import { TagFilter } from "../models/TagFilter";
import { TagFilter, tagFilterFromString } from "../models/TagFilter";

import { locations, technologies, misc } from "../utils/predefined";

Expand Down Expand Up @@ -57,6 +58,15 @@ const HnJobs = () => {
console.info("Changing color mode to: ", colorScheme);
setIsDarkMode(true);
});

// Add custom filters from local storage
const customFilters = localStorage.getItem("CustomFilters")
if (customFilters) {
const restoredFilters = JSON.parse(customFilters).map((f: string) => tagFilterFromString(f))
console.log("Restoring custom filters: ", restoredFilters)
predefinedFilterTags.set("Custom", HSet.fromIterable(restoredFilters));
}

}, []);

return (
Expand Down
43 changes: 22 additions & 21 deletions frontend/src/components/TagFilterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function TagButton({ tagFilter, filterKey, isActive, onActiveChange }: TagProps)

TagButton.defaultProps = {
isActive: false,
onActiveChange: () => {}
onActiveChange: () => { }
}

interface TagFilterProps {
Expand All @@ -32,28 +32,29 @@ interface TagFilterProps {
onSearch: (needle: string | undefined) => void
}

function TagFilterBar({allTags, activeTags, onActive, onInactive, onTagAdd, onSearch}: TagFilterProps) {
// TODO: Drawer instead of Collapsible?
function TagFilterBar({ allTags, activeTags, onActive, onInactive, onTagAdd, onSearch }: TagFilterProps) {
const tagSort = (t1: TagF, t2: TagF): number => {
if(t1.name == "Remote") return -1
if(t2.name == "Remote") return 1
if (t1.name == "Remote") return -1
if (t2.name == "Remote") return 1

return (t1.name < t2.name) ? -1 : 1
}

const filterNonEmpty = Array.from(activeTags.keys()).reduce((acc, key) => acc || Array.from(activeTags.get(key) ?? []).length > 0, false)
const panelStyle: CSSProperties = { border: 'none' };

const activeFiltersDisplay = (filterNonEmpty) ? <>
<h3>Active Filter</h3>
<Flex wrap="wrap" gap="small" className="filter-list">
{Array.from(activeTags.keys()).map((key) =>
{Array.from(activeTags.keys()).map((key) =>
Array.from(activeTags.get(key) ?? []).length > 0 ? (
Array.from(activeTags.get(key) ?? HSet.empty()).sort(tagSort).map(tag =>
<TagButton key={key+tag.name} filterKey={key} tagFilter={tag} isActive={true} onActiveChange={onInactive} />)
Array.from(activeTags.get(key) ?? HSet.empty()).sort(tagSort).map(tag =>
<TagButton key={key + tag.name} filterKey={key} tagFilter={tag} isActive={true} onActiveChange={onInactive} />)
) : <Fragment key={key}></Fragment>
)}
</Flex>
</> : <></>
</> : <></>

const collapsableTagFilters: CollapseProps['items'] = (
Array.from(allTags.keys()).map((tagKey) => (
Expand All @@ -62,8 +63,8 @@ function TagFilterBar({allTags, activeTags, onActive, onInactive, onTagAdd, onSe
label: <h3 className="filter" key={`${tagKey}-header`}>{tagKey}</h3>,
children: (
<Flex key={tagKey} wrap="wrap" gap="small" className="filter-list">
{ Array.from(HSet.difference(allTags.get(tagKey) ?? HSet.empty(), activeTags.get(tagKey) ?? HSet.empty())).sort(tagSort).map(tag =>
<TagButton key={tagKey+tag.name} filterKey={tagKey} tagFilter={tag} isActive={false} onActiveChange={onActive} />)
{Array.from(HSet.difference(allTags.get(tagKey) ?? HSet.empty(), activeTags.get(tagKey) ?? HSet.empty())).sort(tagSort).map(tag =>
<TagButton key={tagKey + tag.name} filterKey={tagKey} tagFilter={tag} isActive={false} onActiveChange={onActive} />)
}
</Flex>
),
Expand All @@ -73,18 +74,18 @@ function TagFilterBar({allTags, activeTags, onActive, onInactive, onTagAdd, onSe
)

const collapsableCustomFilter: CollapseProps['items'] = [
{
key: "custom",
label: <h3 className="filter">Custom Filters</h3>,
children: <CustomFilters onTagAdd={onTagAdd} onSearch={onSearch} />,
style: panelStyle
}
]
{
key: "custom",
label: <h3 className="filter">Custom Filters</h3>,
children: <CustomFilters onTagAdd={onTagAdd} onSearch={onSearch} />,
style: panelStyle
}
]

return <div className="filter-bar">
{activeFiltersDisplay}
<Collapse items={[...collapsableTagFilters, ...collapsableCustomFilter]} bordered={false} />
</div>
</div>
}

export { TagFilterBar, TagButton }
12 changes: 11 additions & 1 deletion frontend/src/models/TagFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ export interface TagFilter {
export const TagFilter = Data.case<TagFilter>()
export const TagFilterSimple = (tagName: string) => TagFilter({name: tagName, pattern: RegExp(`(?:^|\\b|\\s)(?:${tagName})(?:$|\\b|\\s)`, "gmi")})


export const tagFilterToString = (tag: TagFilter): string => JSON.stringify({
name: tag.name,
pattern: {
source: tag.pattern.source,
flags: tag.pattern.flags,
}
})
export const tagFilterFromString = (tagStr: string): TagFilter => {
const parsedJson = JSON.parse(tagStr)
return TagFilter({name: parsedJson.name, pattern: RegExp(parsedJson.pattern.source, parsedJson.pattern.flags)})
}

export type TagFilters = HashSet<TagFilter>
1 change: 0 additions & 1 deletion frontend/src/utils/hn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,3 @@ const itemFilter = (items: Item[], tagFilters: TagFilter[], searchFilter: string
const flatFilters = (filters: Map<string, TagFilters>): TagFilter[] => Array.from(filters.values()).map(filterSet => Array.from(filterSet)).flat()

export { filterByRegex, filterByRegexAny, flatFilters, getItemFromId, getItemsFromIds, getItemsFromQueryId, getItemsFromQueryIds, getKidItemsFromIds, itemFilter, replaceTagCaptureGroup };

0 comments on commit 0c7d0b6

Please sign in to comment.