tangled
alpha
login
or
join now
leaflet.pub
/
leaflet
289
fork
atom
a tool for shared writing and social publishing
289
fork
atom
overview
issues
27
pulls
pipelines
use scoped context for interactionsdrawer
awarm.space
6 months ago
ee0a141e
b3403a37
+89
-27
6 changed files
expand all
collapse all
unified
split
app
lish
[did]
[publication]
[rkey]
Interactions
Comments
CommentBox.tsx
index.tsx
Interactions.tsx
Quotes.tsx
PostPage.tsx
QuoteHandler.tsx
+5
-5
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentBox.tsx
···
19
19
import { publishComment } from "./commentAction";
20
20
import { ButtonPrimary } from "components/Buttons";
21
21
import { ShareSmall } from "components/Icons/ShareSmall";
22
22
-
import { useInteractionState } from "../Interactions";
22
22
+
import { useInteractionState, setInteractionState } from "../Interactions";
23
23
import { DotLoader } from "components/utils/DotLoader";
24
24
import { rangeHasMark } from "src/utils/prosemirror/rangeHasMark";
25
25
import { setMark } from "src/utils/prosemirror/setMark";
···
43
43
autoFocus?: boolean;
44
44
}) {
45
45
let mountRef = useRef<HTMLPreElement | null>(null);
46
46
-
let quote = useInteractionState((s) => s.commentBox.quote);
46
46
+
let { commentBox: { quote } } = useInteractionState(props.doc_uri);
47
47
let [editorState, setEditorState] = useState(() =>
48
48
EditorState.create({
49
49
schema: multiBlockSchema,
···
118
118
if (!quoteParam) return;
119
119
const quotePosition = decodeQuotePosition(quoteParam);
120
120
if (!quotePosition) return;
121
121
-
useInteractionState.setState({
121
121
+
setInteractionState(props.doc_uri, {
122
122
commentBox: { quote: quotePosition },
123
123
});
124
124
return true;
···
165
165
<button
166
166
className="text-border absolute -top-3 right-1 bg-bg-page p-1 rounded-full"
167
167
onClick={() =>
168
168
-
useInteractionState.setState({ commentBox: { quote: null } })
168
168
+
setInteractionState(props.doc_uri, { commentBox: { quote: null } })
169
169
}
170
170
>
171
171
<CloseFillTiny />
···
230
230
view.current?.dispatch(tr);
231
231
setLoading(false);
232
232
props.onSubmit?.();
233
233
-
useInteractionState.setState((s) => ({
233
233
+
setInteractionState(props.doc_uri, (s) => ({
234
234
commentBox: {
235
235
quote: null,
236
236
},
+3
-3
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx
···
1
1
"use client";
2
2
import { CloseTiny } from "components/Icons/CloseTiny";
3
3
-
import { useInteractionState } from "../Interactions";
3
3
+
import { useInteractionState, setInteractionState } from "../Interactions";
4
4
import { useIdentityData } from "components/IdentityProvider";
5
5
import { CommentBox } from "./CommentBox";
6
6
import { Json } from "supabase/database.types";
···
25
25
};
26
26
export function Comments(props: { document_uri: string; comments: Comment[] }) {
27
27
let { identity } = useIdentityData();
28
28
-
let localComments = useInteractionState((l) => l.localComments);
28
28
+
let { localComments } = useInteractionState(props.document_uri);
29
29
let comments = useMemo(() => {
30
30
return [...localComments, ...props.comments];
31
31
}, [props.comments, localComments]);
···
44
44
Comments
45
45
<button
46
46
className="text-tertiary"
47
47
-
onClick={() => useInteractionState.setState({ drawerOpen: false })}
47
47
+
onClick={() => setInteractionState(props.document_uri, { drawerOpen: false })}
48
48
>
49
49
<CloseTiny />
50
50
</button>
+68
-13
app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx
···
6
6
import { create } from "zustand";
7
7
import type { Comment } from "./Comments";
8
8
import { QuotePosition } from "../quotePosition";
9
9
+
import { useContext } from "react";
10
10
+
import { PostPageContext } from "../PostPageContext";
9
11
10
10
-
export let useInteractionState = create(() => ({
11
11
-
drawerOpen: undefined as boolean | undefined,
12
12
-
drawer: undefined as undefined | "comments" | "quotes",
13
13
-
localComments: [] as Comment[],
14
14
-
commentBox: { quote: null as QuotePosition | null },
15
15
-
}));
16
16
-
export function openInteractionDrawer(drawer: "comments" | "quotes") {
12
12
+
type InteractionState = {
13
13
+
drawerOpen: undefined | boolean;
14
14
+
drawer: undefined | "comments" | "quotes";
15
15
+
localComments: Comment[];
16
16
+
commentBox: { quote: QuotePosition | null };
17
17
+
};
18
18
+
19
19
+
const defaultInteractionState: InteractionState = {
20
20
+
drawerOpen: undefined,
21
21
+
drawer: undefined,
22
22
+
localComments: [],
23
23
+
commentBox: { quote: null },
24
24
+
};
25
25
+
26
26
+
export let useInteractionStateStore = create<{
27
27
+
[document_uri: string]: InteractionState;
28
28
+
}>(() => ({}));
29
29
+
30
30
+
export function useInteractionState(document_uri?: string) {
31
31
+
return useInteractionStateStore((state) => {
32
32
+
if (!document_uri || !state[document_uri]) {
33
33
+
return defaultInteractionState;
34
34
+
}
35
35
+
return state[document_uri];
36
36
+
});
37
37
+
}
38
38
+
39
39
+
export function setInteractionState(
40
40
+
document_uri: string,
41
41
+
update:
42
42
+
| Partial<InteractionState>
43
43
+
| ((state: InteractionState) => Partial<InteractionState>),
44
44
+
) {
45
45
+
useInteractionStateStore.setState((state) => {
46
46
+
if (!state[document_uri]) {
47
47
+
state[document_uri] = { ...defaultInteractionState };
48
48
+
}
49
49
+
50
50
+
const currentDocState = state[document_uri];
51
51
+
const updatedState =
52
52
+
typeof update === "function" ? update(currentDocState) : update;
53
53
+
54
54
+
return {
55
55
+
...state,
56
56
+
[document_uri]: {
57
57
+
...currentDocState,
58
58
+
...updatedState,
59
59
+
},
60
60
+
};
61
61
+
});
62
62
+
}
63
63
+
export function openInteractionDrawer(
64
64
+
drawer: "comments" | "quotes",
65
65
+
document_uri: string,
66
66
+
) {
17
67
flushSync(() => {
18
18
-
useInteractionState.setState({ drawerOpen: true, drawer });
68
68
+
setInteractionState(document_uri, { drawerOpen: true, drawer });
19
69
});
20
70
let el = document.getElementById("interaction-drawer");
21
71
let isOffscreen = false;
···
38
88
className?: string;
39
89
showComments?: boolean;
40
90
}) => {
41
41
-
let { drawerOpen, drawer } = useInteractionState();
91
91
+
const data = useContext(PostPageContext);
92
92
+
const document_uri = data?.uri;
93
93
+
if (!document_uri)
94
94
+
throw new Error("document_uri not available in PostPageContext");
95
95
+
96
96
+
let { drawerOpen, drawer } = useInteractionState(document_uri);
42
97
43
98
return (
44
99
<div
···
48
103
className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`}
49
104
onClick={() => {
50
105
if (!drawerOpen || drawer !== "quotes")
51
51
-
openInteractionDrawer("quotes");
52
52
-
else useInteractionState.setState({ drawerOpen: false });
106
106
+
openInteractionDrawer("quotes", document_uri);
107
107
+
else setInteractionState(document_uri, { drawerOpen: false });
53
108
}}
54
109
>
55
110
<QuoteTiny /> {props.quotesCount}{" "}
···
60
115
className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`}
61
116
onClick={() => {
62
117
if (!drawerOpen || drawer !== "comments")
63
63
-
openInteractionDrawer("comments");
64
64
-
else useInteractionState.setState({ drawerOpen: false });
118
118
+
openInteractionDrawer("comments", document_uri);
119
119
+
else setInteractionState(document_uri, { drawerOpen: false });
65
120
}}
66
121
>
67
122
<CommentTiny /> {props.commentsCount}{" "}
+4
-2
app/lish/[did]/[publication]/[rkey]/Interactions/Quotes.tsx
···
2
2
import { CloseTiny } from "components/Icons/CloseTiny";
3
3
import { useContext } from "react";
4
4
import { useIsMobile } from "src/hooks/isMobile";
5
5
-
import { useInteractionState } from "./Interactions";
5
5
+
import { setInteractionState } from "./Interactions";
6
6
import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs";
7
7
import { AtUri } from "@atproto/api";
8
8
import { Json } from "supabase/database.types";
···
25
25
did: string;
26
26
}) => {
27
27
let data = useContext(PostPageContext);
28
28
+
const document_uri = data?.uri;
29
29
+
if (!document_uri) throw new Error('document_uri not available in PostPageContext');
28
30
29
31
return (
30
32
<div className="flex flex-col gap-2">
···
32
34
Quotes
33
35
<button
34
36
className="text-tertiary"
35
35
-
onClick={() => useInteractionState.setState({ drawerOpen: false })}
37
37
+
onClick={() => setInteractionState(document_uri, { drawerOpen: false })}
36
38
>
37
39
<CloseTiny />
38
40
</button>
+2
-1
app/lish/[did]/[publication]/[rkey]/PostPage.tsx
···
36
36
preferences: { showComments?: boolean };
37
37
}) {
38
38
let { identity } = useIdentityData();
39
39
-
let { drawerOpen } = useInteractionState();
39
39
+
const document_uri = document?.uri;
40
40
+
let { drawerOpen } = useInteractionState(document_uri);
40
41
if (!document || !document.documents_in_publications[0].publications)
41
42
return null;
42
43
+7
-3
app/lish/[did]/[publication]/[rkey]/QuoteHandler.tsx
···
3
3
import { CopyTiny } from "components/Icons/CopyTiny";
4
4
import { Separator } from "components/Layout";
5
5
import { useSmoker } from "components/Toast";
6
6
-
import { useEffect, useMemo, useState } from "react";
6
6
+
import { useEffect, useMemo, useState, useContext } from "react";
7
7
import {
8
8
encodeQuotePosition,
9
9
decodeQuotePosition,
···
11
11
} from "./quotePosition";
12
12
import { useIdentityData } from "components/IdentityProvider";
13
13
import { CommentTiny } from "components/Icons/CommentTiny";
14
14
-
import { useInteractionState } from "./Interactions/Interactions";
14
14
+
import { setInteractionState } from "./Interactions/Interactions";
15
15
+
import { PostPageContext } from "./PostPageContext";
15
16
16
17
export function QuoteHandler() {
17
18
let [position, setPosition] = useState<{
···
128
129
export const QuoteOptionButtons = (props: { position: string }) => {
129
130
let smoker = useSmoker();
130
131
let { identity } = useIdentityData();
132
132
+
const data = useContext(PostPageContext);
133
133
+
const document_uri = data?.uri;
134
134
+
if (!document_uri) throw new Error('document_uri not available in PostPageContext');
131
135
let [url, position] = useMemo(() => {
132
136
let currentUrl = new URL(window.location.href);
133
137
let pos = decodeQuotePosition(props.position);
···
186
190
className="flex gap-1 items-center hover:font-bold px-1"
187
191
onClick={() => {
188
192
if (!position) return;
189
189
-
useInteractionState.setState({
193
193
+
setInteractionState(document_uri, {
190
194
drawer: "comments",
191
195
drawerOpen: true,
192
196
commentBox: { quote: position },