feat(journal): add JournalContext to allow closing panel on mobile

This commit is contained in:
hypercross 2026-07-08 00:11:16 +08:00
parent 57ffb35f2b
commit 3d957706e9
4 changed files with 68 additions and 39 deletions

View File

@ -0,0 +1,18 @@
/**
* Journal context provides `onClose` to message renderers so that
* clicking a link message can close the panel on mobile.
*/
import { createContext, useContext } from "solid-js";
export interface JournalContextValue {
onClose: () => void;
}
const JournalContext = createContext<JournalContextValue>();
export function useJournalContext(): JournalContextValue | undefined {
return useContext(JournalContext);
}
export { JournalContext };

View File

@ -26,6 +26,7 @@ import {
} from "../stores/journalStream"; } from "../stores/journalStream";
import { StreamView } from "./StreamView"; import { StreamView } from "./StreamView";
import { JournalInput } from "./JournalInput"; import { JournalInput } from "./JournalInput";
import { JournalContext } from "./JournalContext";
export interface JournalPanelProps { export interface JournalPanelProps {
open: boolean; open: boolean;
@ -57,8 +58,9 @@ export const JournalPanel: Component<JournalPanelProps> = (props) => {
transition-transform duration-300 ease-in-out ${ transition-transform duration-300 ease-in-out ${
props.open ? "translate-x-0" : "translate-x-full" props.open ? "translate-x-0" : "translate-x-full"
}`} }`}
aria-hidden={!props.open} inert={!props.open}
> >
<JournalContext.Provider value={{ onClose: props.onClose }}>
{/* Header */} {/* Header */}
<JournalHeader onClose={props.onClose} /> <JournalHeader onClose={props.onClose} />
@ -100,6 +102,7 @@ export const JournalPanel: Component<JournalPanelProps> = (props) => {
<Show when={showInvite()}> <Show when={showInvite()}>
<InviteDialog onClose={() => setShowInvite(false)} /> <InviteDialog onClose={() => setShowInvite(false)} />
</Show> </Show>
</JournalContext.Provider>
</aside> </aside>
</> </>
); );

View File

@ -44,3 +44,5 @@ export { ComposePanel } from "./ComposePanel";
export { JournalInput } from "./JournalInput"; export { JournalInput } from "./JournalInput";
export { DynamicForm } from "./DynamicForm"; export { DynamicForm } from "./DynamicForm";
export { useJournalCompletions, invalidateCompletions } from "./completions"; export { useJournalCompletions, invalidateCompletions } from "./completions";
export { JournalContext, useJournalContext } from "./JournalContext";
export type { JournalContextValue } from "./JournalContext";

View File

@ -11,6 +11,7 @@ import { z } from "zod";
import { registerMessageType } from "../registry"; import { registerMessageType } from "../registry";
import { journalSetState } from "../../stores/journalStream"; import { journalSetState } from "../../stores/journalStream";
import { produce } from "solid-js/store"; import { produce } from "solid-js/store";
import { useJournalContext } from "../JournalContext";
const schema = z.object({ const schema = z.object({
path: z.string().min(1), path: z.string().min(1),
@ -44,6 +45,7 @@ function slugToTitle(slug: string): string {
const RevealLink: Component<LinkPayload> = (p) => { const RevealLink: Component<LinkPayload> = (p) => {
const navigate = useNavigateWithParams(); const navigate = useNavigateWithParams();
const ctx = useJournalContext();
const target = () => { const target = () => {
let t = encodePath(p.path); let t = encodePath(p.path);
@ -59,6 +61,10 @@ const RevealLink: Component<LinkPayload> = (p) => {
const handleClick = (e: MouseEvent) => { const handleClick = (e: MouseEvent) => {
e.preventDefault(); e.preventDefault();
navigate(target()); navigate(target());
// On mobile, close the journal panel to reveal the article
if (window.innerWidth < 768) {
ctx?.onClose();
}
}; };
return ( return (