diff --git a/src/App.tsx b/src/App.tsx
index 1f31bfa..80e3b4f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,14 +1,25 @@
import { Box, Flex, Spinner } from "@chakra-ui/react";
import { useAtomValue } from "jotai";
-import { Suspense } from "react";
+import { Suspense, useMemo } from "react";
import { Header } from "./components/Header";
import { LoginPane } from "./components/LoginPane";
import { PostTimeline } from "./components/PostTimeline";
import { WaybackQueryForm } from "./components/WaybackQueryForm";
+import type { PostOrder } from "./states/Posts";
import { myPubkeyAtom } from "./states/Profiles";
+import { waybackQueryInputsAtom } from "./states/WaybackQuery";
export const App = () => {
const myPubkey = useAtomValue(myPubkeyAtom);
+ const queryInputs = useAtomValue(waybackQueryInputsAtom);
+
+ const postOrder: PostOrder = useMemo(() => {
+ if (queryInputs?.type === "since-and-until") {
+ // show oldest first when since < until (natural order), newest first when reversed
+ return queryInputs.sinceDatetime <= queryInputs.untilDatetime ? "created-at-asc" : "created-at-desc";
+ }
+ return "created-at-desc";
+ }, [queryInputs]);
return (
<>
@@ -20,7 +31,7 @@ export const App = () => {
{myPubkey !== "" && (
-
+
)}
diff --git a/src/components/WaybackQueryForm.tsx b/src/components/WaybackQueryForm.tsx
index e98e188..b0de2d9 100644
--- a/src/components/WaybackQueryForm.tsx
+++ b/src/components/WaybackQueryForm.tsx
@@ -45,10 +45,12 @@ export const WaybackQueryForm: React.FC = () => {
const setQueryInputs = useSetAtom(waybackQueryInputsAtom);
const sinceAndDurForm = useSinceAndDurForm();
+ const sinceAndUntilForm = useSinceAndUntilForm();
const untilNowForm = useUntilNowForm();
const tabs = [
{ key: "since-dur", label: "始点+期間", form: sinceAndDurForm },
+ { key: "since-until", label: "始点+終点", form: sinceAndUntilForm },
{ key: "until-now", label: "現在まで", form: untilNowForm },
];
const [tabIdx, setTabIdx] = useState(0);
@@ -156,6 +158,68 @@ const useSinceAndDurForm = () => {
};
};
+const useSinceAndUntilForm = () => {
+ const now = getNow();
+ const [sinceDate, setSinceDate] = useState(subHours(now, 1));
+ const [sinceTime, setSinceTime] = useState(format(subHours(now, 1), "HH:mm"));
+ const [untilDate, setUntilDate] = useState(now);
+ const [untilTime, setUntilTime] = useState(format(now, "HH:mm"));
+
+ const queryInputs: WaybackQueryInputs | undefined = useMemo(() => {
+ const sinceDatetime = `${format(sinceDate, "yyyy-MM-dd")}T${sinceTime}`;
+ const untilDatetime = `${format(untilDate, "yyyy-MM-dd")}T${untilTime}`;
+ return {
+ type: "since-and-until",
+ sinceDatetime,
+ untilDatetime,
+ };
+ }, [sinceDate, sinceTime, untilDate, untilTime]);
+
+ const view = (
+
+
+
+ setSinceTime(e.target.value)} w="120px" />
+ から
+
+
+
+ setUntilTime(e.target.value)} w="120px" />
+ まで
+
+
+ );
+
+ return {
+ queryInputs,
+ view,
+ };
+};
+
const agoTimeUnitLabels: Record = {
minutes: "分",
hours: "時間",
diff --git a/src/types/WaybackQuery.ts b/src/types/WaybackQuery.ts
index 325bd41..7126976 100644
--- a/src/types/WaybackQuery.ts
+++ b/src/types/WaybackQuery.ts
@@ -11,11 +11,17 @@ type WBQueryInputsSinceAndDur = {
sinceDatetime: string; // yyyy-MM-ddTHH:mm
} & DurationInputs;
+type WBQueryInputsSinceAndUntil = {
+ type: "since-and-until";
+ sinceDatetime: string; // yyyy-MM-ddTHH:mm
+ untilDatetime: string; // yyyy-MM-ddTHH:mm
+};
+
type WBQueryInputsUntilNow = {
type: "until-now";
} & DurationInputs;
-export type WaybackQueryInputs = WBQueryInputsSinceAndDur | WBQueryInputsUntilNow;
+export type WaybackQueryInputs = WBQueryInputsSinceAndDur | WBQueryInputsSinceAndUntil | WBQueryInputsUntilNow;
type WBQueryInputsTypes = WaybackQueryInputs["type"];
@@ -52,6 +58,9 @@ const stringifyDurationInputs = ({ durationValue: value, durationUnit: unit }: D
};
const detectWBQueryType = (urlParams: URLSearchParams): WBQueryInputsTypes | undefined => {
+ if (urlParams.has("since") && urlParams.has("until")) {
+ return "since-and-until";
+ }
if (urlParams.has("since") && (urlParams.has("dur") || urlParams.has("len"))) {
return "since-and-dur";
}
@@ -81,6 +90,21 @@ const sinceAndDurfromURLQuery = (params: URLSearchParams): WaybackQueryInputs |
};
};
+const sinceAndUntilfromURLQuery = (params: URLSearchParams): WaybackQueryInputs | undefined => {
+ const sinceStr = params.get("since");
+ const untilStr = params.get("until");
+
+ if (!sinceStr || !untilStr) {
+ return undefined;
+ }
+
+ return {
+ type: "since-and-until",
+ sinceDatetime: sinceStr,
+ untilDatetime: untilStr,
+ };
+};
+
const untilNowfromURLQuery = (params: URLSearchParams): WaybackQueryInputs | undefined => {
const agoStr = params.get("ago");
if (!agoStr) {
@@ -108,6 +132,8 @@ export const WaybackQueryInputs = {
switch (wbQueryType) {
case "since-and-dur":
return sinceAndDurfromURLQuery(params);
+ case "since-and-until":
+ return sinceAndUntilfromURLQuery(params);
case "until-now":
return untilNowfromURLQuery(params);
}
@@ -120,6 +146,11 @@ export const WaybackQueryInputs = {
params.set("dur", stringifyDurationInputs(inputs));
break;
}
+ case "since-and-until": {
+ params.set("since", inputs.sinceDatetime);
+ params.set("until", inputs.untilDatetime);
+ break;
+ }
case "until-now": {
params.set("ago", stringifyDurationInputs(inputs));
break;
@@ -171,6 +202,26 @@ export const WaybackQuery = {
const until = Math.min(since + durationInSecs(inputs), getUnixTime(new Date()));
return { since, until };
}
+ case "since-and-until": {
+ let since: number;
+ let until: number;
+ try {
+ since = getUnixTime(parseISO(inputs.sinceDatetime));
+ until = getUnixTime(parseISO(inputs.untilDatetime));
+ } catch (err) {
+ console.error("fromInputs: invalid datetime:", err);
+ return undefined;
+ }
+ if (since === until) {
+ return undefined;
+ }
+ // swap if since > until (user entered times in reverse order)
+ if (since > until) {
+ [since, until] = [until, since];
+ }
+ until = Math.min(until, getUnixTime(new Date()));
+ return { since, until };
+ }
case "until-now": {
if (inputs.durationValue === 0) {
return undefined;