Skip to content

Commit

Permalink
Mail search by category
Browse files Browse the repository at this point in the history
Close #5
  • Loading branch information
nonylene committed Feb 9, 2024
1 parent 231378b commit ef3529d
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 251 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ HEINEKEN_ELASTIC_SEARCH_SCRAPBOX_INDEX="scrapbox"
HEINEKEN_PUKIWIKI_BASE_URL="https://inside.kmc.gr.jp/wiki/"

HEINEKEN_MAIL_DEFAULT_CATEGORIES="info,"
HEINEKEN_MAIL_CATEGORIES="info,spam,"
HEINEKEN_MAIL_BASE_URL="https://inside.kmc.gr.jp/m2w/"

HEINEKEN_SCRAPBOX_BASE_URL="https://scrapbox.io/kmc/"
File renamed without changes.
132 changes: 132 additions & 0 deletions app/components/search-box/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import styles from "./index.css";
import {
faCircleQuestion,
faMagnifyingGlass,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { LinksFunction } from "@remix-run/node";
import { Form, Link, useSubmit } from "@remix-run/react";
import { useEffect, useRef, useState } from "react";
import Select from "react-select";

export const links: LinksFunction = () => [{ rel: "stylesheet", href: styles }];

interface TypesOption {
value: string;
label: string;
}

const getTypesOptions = (types: TypesOption[], values: string[]) =>
values.map((v) => {
const type = types.find(({ value }) => value === v);
if (type === undefined) {
throw Error(`value ${v} is not found on typesOption`);
}
return type;
});

interface SearchBoxProps {
order: string;
types: TypesOption[];
typeInputName: string;
defaultTypes: string[];
defaultAdvanced: boolean;
defaultQuery: string;
action: string;
}

export function SearchBox(props: SearchBoxProps) {
const submit = useSubmit();
const formRef = useRef<HTMLFormElement>(null);

const [advanced, setAdvanced] = useState(props.defaultAdvanced);
const [query, setQuery] = useState(props.defaultQuery);
const [types, setTypes] = useState(props.defaultTypes);

useEffect(() => setQuery(props.defaultQuery), [props.defaultQuery]);
useEffect(() => setAdvanced(props.defaultAdvanced), [props.defaultAdvanced]);
useEffect(() => {
setTypes(props.defaultTypes);
// array をちゃんと比較してレンダリング回数を抑えるために JSON にする
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(props.defaultTypes)]);

// _root.tsx でエラーハンドリングがなされると(例: 404)emotion が追加した style tag が外れて悲惨な見た目になるが、
// そう起きないと信じて無視する
// https://github.com/remix-run/remix/issues/1136
return (
<div className="SearchBox row">
<div className="col-md-8 offset-md-2 mt-4 mt-sm-5 mb-4">
<Form action={props.action} preventScrollReset ref={formRef}>
<div className="input-group input-group-lg">
<input
className="form-control"
autoFocus={true}
type="text"
name="query"
placeholder={advanced ? "Input raw query string..." : "Search"}
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<input type="hidden" name="order" value={props.order} />
<button className="btn btn-outline-secondary" type="submit">
<FontAwesomeIcon icon={faMagnifyingGlass} />
</button>
</div>
<div className="row">
<div className="col-auto mt-3 ms-1 align-items-center d-flex">
<div className="form-switch form-check">
<input
className="form-check-input"
type="checkbox"
role="switch"
name="advanced"
id="searchBoxCheckBox"
checked={advanced}
onChange={(e) => setAdvanced(e.target.checked)}
/>
<label className="form-check-label" htmlFor="searchBoxCheckBox">
Advanced
<span className="d-none d-sm-inline-block">&nbsp;mode</span>
&ensp;
<Link to="/help" id="questionMarkLink" target="_blank">
<FontAwesomeIcon
icon={faCircleQuestion}
id="questionMark"
size="sm"
/>
</Link>
</label>
</div>
</div>
<div className="col-auto mt-3 ms-auto me-1 text-end ps-0">
<Select
options={props.types}
isMulti={true}
isClearable={false}
closeMenuOnSelect={false}
value={getTypesOptions(props.types, types)}
name={props.typeInputName}
onChange={(e) => {
// Select の中で値が変わっても、form の onChange は発火しない + submit すると古い値が使われる
// ので、現在のフォームの値を持ってきて上書きした上で submit する
let newTypes = e!.map((option) => option.value);
if (newTypes.length === 0) {
newTypes = [props.types[0].value];
}
const data = new FormData(formRef.current!);
data.delete(props.typeInputName);
newTypes.forEach((t) => data.append(props.typeInputName, t));
submit(data);
setTypes(newTypes); // UI 上ですぐに変えるため
}}
components={{ IndicatorSeparator: () => null }}
isSearchable={false}
/>
</div>
</div>
</Form>
</div>
</div>
);
}
20 changes: 17 additions & 3 deletions app/routes/search.mail/route.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {
SearchBox,
links as searchBoxLinks,
} from "../../components/search-box";
import { SEARCH_SIZE, buildMailSearch, requestSearch } from "./els-client";
import { MessageList, links as pageListLinks } from "./message-list";
import { MessageResult } from "./models";
import { SearchBox, links as searchBoxLinks } from "./search-box";
import { parseSearchParams, setNewOrder, setNewPage } from "./utils";
import {
LinksFunction,
Expand Down Expand Up @@ -56,11 +59,22 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
});
};

const createSearchBox = (params: URLSearchParams) => {
const { query, order, advanced } = parseSearchParams(params);
const createSearchBox = (
params: URLSearchParams,
defaultCategories: string[],
allCategories: string[],
) => {
const { query, order, advanced, categories } = parseSearchParams(params);
const types = allCategories.map((v) => {
return { value: v, label: v };
});
return (
<SearchBox
order={order}
types={types}
action={"/search/mail"}
defaultTypes={categories ?? defaultCategories}
typeInputName="category"
defaultQuery={query || ""}
defaultAdvanced={advanced}
/>
Expand Down
70 changes: 0 additions & 70 deletions app/routes/search.mail/search-box.tsx

This file was deleted.

6 changes: 5 additions & 1 deletion app/routes/search.pukiwiki/route.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SEARCH_SIZE, buildPukiWikiSearch, requestSearch } from "./els-client";
import { PageResult } from "./models";
import { PageList, links as pageListLinks } from "./page-list";
import { SearchBox, links as searchBoxLinks } from "./search-box";
import { parseSearchParams, setNewOrder, setNewPage } from "./utils";
import {
LinksFunction,
Expand All @@ -20,6 +19,7 @@ import {
import { Suspense, useContext } from "react";
import HeinekenError from "~/components/heineken-error";
import { Pager, links as pagerLinks } from "~/components/pager";
import { SearchBox, links as searchBoxLinks } from "~/components/search-box";
import SortButton from "~/components/sort-button";
import { StatusIndicator } from "~/components/status-indicator";
import { EnvContext } from "~/contexts/env";
Expand Down Expand Up @@ -59,6 +59,10 @@ const createSearchBox = (params: URLSearchParams) => {
return (
<SearchBox
order={order}
types={[]}
action={"/search/pukiwiki"}
defaultTypes={[]}
typeInputName="type"
defaultQuery={query || ""}
defaultAdvanced={advanced}
/>
Expand Down
18 changes: 0 additions & 18 deletions app/routes/search.pukiwiki/search-box.css

This file was deleted.

70 changes: 0 additions & 70 deletions app/routes/search.pukiwiki/search-box.tsx

This file was deleted.

6 changes: 5 additions & 1 deletion app/routes/search.scrapbox/route.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { SEARCH_SIZE, buildScrapboxSearch, requestSearch } from "./els-client";
import { PageResult } from "./models";
import { PageList, links as pageListLinks } from "./page-list";
import { SearchBox, links as searchBoxLinks } from "./search-box";
import { parseSearchParams, setNewOrder, setNewPage } from "./utils";
import {
LinksFunction,
Expand All @@ -20,6 +19,7 @@ import {
import { Suspense, useContext } from "react";
import HeinekenError from "~/components/heineken-error";
import { Pager, links as pagerLinks } from "~/components/pager";
import { SearchBox, links as searchBoxLinks } from "~/components/search-box";
import SortButton from "~/components/sort-button";
import { StatusIndicator } from "~/components/status-indicator";
import { EnvContext } from "~/contexts/env";
Expand Down Expand Up @@ -59,6 +59,10 @@ const createSearchBox = (params: URLSearchParams) => {
return (
<SearchBox
order={order}
types={[]}
action={"/search/scrapbox"}
defaultTypes={[]}
typeInputName="type"
defaultQuery={query || ""}
defaultAdvanced={advanced}
/>
Expand Down
Loading

0 comments on commit ef3529d

Please sign in to comment.