@tightknitai/block-kitchen 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/helpers.cjs +10 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.js +10 -0
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +255 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +256 -90
- package/dist/index.js.map +1 -1
- package/dist/{palette-DIbvAzRA.d.cts → palette-inX8F01o.d.cts} +9 -1
- package/dist/{palette-DIbvAzRA.d.ts → palette-inX8F01o.d.ts} +9 -1
- package/dist/palette.d.cts +1 -1
- package/dist/palette.d.ts +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1917,6 +1917,10 @@ function sanitizeImageSrc(value) {
|
|
|
1917
1917
|
// src/lib/sanitize-blocks.ts
|
|
1918
1918
|
var HREF_KEYS = /* @__PURE__ */ new Set(["url"]);
|
|
1919
1919
|
var IMAGE_KEYS = /* @__PURE__ */ new Set(["image_url"]);
|
|
1920
|
+
var RETRIEVAL_ONLY_KEYS = /* @__PURE__ */ new Map([
|
|
1921
|
+
// Covers both the image block and the image element — both `type: 'image'`.
|
|
1922
|
+
["image", /* @__PURE__ */ new Set(["image_width", "image_height", "image_bytes", "fallback", "is_animated"])]
|
|
1923
|
+
]);
|
|
1920
1924
|
function sanitizeValue(value) {
|
|
1921
1925
|
if (value === null || typeof value !== "object") {
|
|
1922
1926
|
return value;
|
|
@@ -1934,8 +1938,14 @@ function sanitizeValue(value) {
|
|
|
1934
1938
|
return changed ? out : value;
|
|
1935
1939
|
}
|
|
1936
1940
|
const src = value;
|
|
1941
|
+
const dropKeys = typeof src.type === "string" ? RETRIEVAL_ONLY_KEYS.get(src.type) : void 0;
|
|
1937
1942
|
let copy = null;
|
|
1938
1943
|
for (const key of Object.keys(src)) {
|
|
1944
|
+
if (dropKeys?.has(key)) {
|
|
1945
|
+
copy ??= { ...src };
|
|
1946
|
+
delete copy[key];
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1939
1949
|
const original = src[key];
|
|
1940
1950
|
let next = original;
|
|
1941
1951
|
if (typeof original === "string") {
|
|
@@ -1959,6 +1969,18 @@ function sanitizeValue(value) {
|
|
|
1959
1969
|
function sanitizeBlock(block) {
|
|
1960
1970
|
return sanitizeValue(block);
|
|
1961
1971
|
}
|
|
1972
|
+
function sanitizeBlocks(blocks) {
|
|
1973
|
+
let changed = false;
|
|
1974
|
+
const out = new Array(blocks.length);
|
|
1975
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
1976
|
+
const next = sanitizeBlock(blocks[i]);
|
|
1977
|
+
if (next !== blocks[i]) {
|
|
1978
|
+
changed = true;
|
|
1979
|
+
}
|
|
1980
|
+
out[i] = next;
|
|
1981
|
+
}
|
|
1982
|
+
return changed ? out : blocks;
|
|
1983
|
+
}
|
|
1962
1984
|
|
|
1963
1985
|
// src/lib/to-slack-blocks.ts
|
|
1964
1986
|
function toSlackBlocks(blocks) {
|
|
@@ -2216,7 +2238,7 @@ function useBlockKitchenState({
|
|
|
2216
2238
|
});
|
|
2217
2239
|
}, []);
|
|
2218
2240
|
const replaceAll = React.useCallback((newBlocks) => {
|
|
2219
|
-
setBlocks(newBlocks.map(wrap));
|
|
2241
|
+
setBlocks(sanitizeBlocks(newBlocks).map(wrap));
|
|
2220
2242
|
}, []);
|
|
2221
2243
|
return {
|
|
2222
2244
|
blocks,
|
|
@@ -2625,8 +2647,10 @@ function JsonDrawer({
|
|
|
2625
2647
|
const [value, setValue] = React.useState("");
|
|
2626
2648
|
const [parseError, setParseError] = React.useState(null);
|
|
2627
2649
|
const [validationErrors, setValidationErrors] = React.useState([]);
|
|
2650
|
+
const [copied, setCopied] = React.useState(false);
|
|
2628
2651
|
const textareaRef = React.useRef(null);
|
|
2629
2652
|
const gutterRef = React.useRef(null);
|
|
2653
|
+
const copyResetRef = React.useRef(null);
|
|
2630
2654
|
const blocksRef = React.useRef(blocks);
|
|
2631
2655
|
React.useEffect(() => {
|
|
2632
2656
|
blocksRef.current = blocks;
|
|
@@ -2636,8 +2660,27 @@ function JsonDrawer({
|
|
|
2636
2660
|
setValue(JSON.stringify(blocksRef.current, null, 2));
|
|
2637
2661
|
setParseError(null);
|
|
2638
2662
|
setValidationErrors([]);
|
|
2663
|
+
setCopied(false);
|
|
2639
2664
|
}
|
|
2640
2665
|
}, [open]);
|
|
2666
|
+
React.useEffect(
|
|
2667
|
+
() => () => {
|
|
2668
|
+
if (copyResetRef.current) {
|
|
2669
|
+
clearTimeout(copyResetRef.current);
|
|
2670
|
+
}
|
|
2671
|
+
},
|
|
2672
|
+
[]
|
|
2673
|
+
);
|
|
2674
|
+
const handleCopy = () => {
|
|
2675
|
+
navigator.clipboard?.writeText(value).then(() => {
|
|
2676
|
+
setCopied(true);
|
|
2677
|
+
if (copyResetRef.current) {
|
|
2678
|
+
clearTimeout(copyResetRef.current);
|
|
2679
|
+
}
|
|
2680
|
+
copyResetRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
2681
|
+
}).catch(() => {
|
|
2682
|
+
});
|
|
2683
|
+
};
|
|
2641
2684
|
const handleChange = (next) => {
|
|
2642
2685
|
setValue(next);
|
|
2643
2686
|
if (next.length > MAX_JSON_BYTES) {
|
|
@@ -2684,7 +2727,17 @@ function JsonDrawer({
|
|
|
2684
2727
|
/* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { children: "Block Kit JSON" }),
|
|
2685
2728
|
/* @__PURE__ */ jsxRuntime.jsx(SheetDescription, { children: "Edits update the preview as you type. Parse errors show below." })
|
|
2686
2729
|
] }),
|
|
2687
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden rounded-md border border-input bg-muted/30 shadow-sm focus-within:ring-1 focus-within:ring-ring", children: [
|
|
2730
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-1 overflow-hidden rounded-md border border-input bg-muted/30 shadow-sm focus-within:ring-1 focus-within:ring-ring", children: [
|
|
2731
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2732
|
+
"button",
|
|
2733
|
+
{
|
|
2734
|
+
type: "button",
|
|
2735
|
+
onClick: handleCopy,
|
|
2736
|
+
"aria-label": copied ? "Copied to clipboard" : "Copy JSON",
|
|
2737
|
+
className: "absolute right-2 top-2 z-10 inline-flex items-center justify-center rounded-md border border-input bg-background/80 p-1.5 text-muted-foreground shadow-sm backdrop-blur transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
2738
|
+
children: copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3.5 w-3.5 text-emerald-600" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3.5 w-3.5" })
|
|
2739
|
+
}
|
|
2740
|
+
),
|
|
2688
2741
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2689
2742
|
"div",
|
|
2690
2743
|
{
|
|
@@ -2833,6 +2886,79 @@ var Label = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__
|
|
|
2833
2886
|
}
|
|
2834
2887
|
));
|
|
2835
2888
|
Label.displayName = LabelPrimitive__namespace.Root.displayName;
|
|
2889
|
+
var POLL_INTERVAL_MS = 2500;
|
|
2890
|
+
var MAX_POLLS = 24;
|
|
2891
|
+
function useSlackSignIn(loadStatus, { open, enabled }) {
|
|
2892
|
+
const [userStatus, setUserStatus] = React.useState(null);
|
|
2893
|
+
const [polling, setPolling] = React.useState(false);
|
|
2894
|
+
const loadStatusRef = React.useRef(loadStatus);
|
|
2895
|
+
React.useEffect(() => {
|
|
2896
|
+
loadStatusRef.current = loadStatus;
|
|
2897
|
+
});
|
|
2898
|
+
const refresh = React.useCallback(() => {
|
|
2899
|
+
loadStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
|
|
2900
|
+
}, []);
|
|
2901
|
+
const pollRef = React.useRef(null);
|
|
2902
|
+
const stopPolling = React.useCallback(() => {
|
|
2903
|
+
if (pollRef.current) {
|
|
2904
|
+
clearInterval(pollRef.current);
|
|
2905
|
+
pollRef.current = null;
|
|
2906
|
+
}
|
|
2907
|
+
setPolling(false);
|
|
2908
|
+
}, []);
|
|
2909
|
+
const startSignIn = React.useCallback(() => {
|
|
2910
|
+
const url = userStatus?.oauthUrl;
|
|
2911
|
+
if (!url || !isSafeHref(url)) {
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
2915
|
+
stopPolling();
|
|
2916
|
+
setPolling(true);
|
|
2917
|
+
let tries = 0;
|
|
2918
|
+
pollRef.current = setInterval(() => {
|
|
2919
|
+
tries += 1;
|
|
2920
|
+
loadStatusRef.current().then((next) => {
|
|
2921
|
+
setUserStatus(next);
|
|
2922
|
+
if (next.canSendAsUser) {
|
|
2923
|
+
stopPolling();
|
|
2924
|
+
}
|
|
2925
|
+
}).catch(() => {
|
|
2926
|
+
}).finally(() => {
|
|
2927
|
+
if (tries >= MAX_POLLS) {
|
|
2928
|
+
stopPolling();
|
|
2929
|
+
}
|
|
2930
|
+
});
|
|
2931
|
+
}, POLL_INTERVAL_MS);
|
|
2932
|
+
}, [userStatus, stopPolling]);
|
|
2933
|
+
React.useEffect(() => {
|
|
2934
|
+
if (!open || !enabled) {
|
|
2935
|
+
setUserStatus(null);
|
|
2936
|
+
stopPolling();
|
|
2937
|
+
return;
|
|
2938
|
+
}
|
|
2939
|
+
setUserStatus(null);
|
|
2940
|
+
refresh();
|
|
2941
|
+
return stopPolling;
|
|
2942
|
+
}, [open, enabled, refresh, stopPolling]);
|
|
2943
|
+
React.useEffect(() => {
|
|
2944
|
+
if (!open || !enabled) {
|
|
2945
|
+
return;
|
|
2946
|
+
}
|
|
2947
|
+
const handler = () => refresh();
|
|
2948
|
+
window.addEventListener("focus", handler);
|
|
2949
|
+
return () => window.removeEventListener("focus", handler);
|
|
2950
|
+
}, [open, enabled, refresh]);
|
|
2951
|
+
return { userStatus, polling, startSignIn };
|
|
2952
|
+
}
|
|
2953
|
+
function SlackSignInButton({ onClick, polling }) {
|
|
2954
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "outline", size: "sm", className: "w-fit", onClick, disabled: polling, children: polling ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2955
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin" }),
|
|
2956
|
+
" Waiting for Slack\u2026"
|
|
2957
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2958
|
+
"Sign in with Slack ",
|
|
2959
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, {})
|
|
2960
|
+
] }) });
|
|
2961
|
+
}
|
|
2836
2962
|
function recentToResult(msg) {
|
|
2837
2963
|
return {
|
|
2838
2964
|
ok: true,
|
|
@@ -2846,6 +2972,17 @@ function recentToResult(msg) {
|
|
|
2846
2972
|
iconUrl: msg.iconUrl
|
|
2847
2973
|
};
|
|
2848
2974
|
}
|
|
2975
|
+
function previewText(m) {
|
|
2976
|
+
if (m.label) {
|
|
2977
|
+
return m.label;
|
|
2978
|
+
}
|
|
2979
|
+
for (const b of m.blocks) {
|
|
2980
|
+
if ((b.type === "header" || b.type === "section") && "text" in b && b.text && "text" in b.text) {
|
|
2981
|
+
return b.text.text;
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
return `${m.blocks.length} block${m.blocks.length === 1 ? "" : "s"}`;
|
|
2985
|
+
}
|
|
2849
2986
|
function LoadMessageDialog({
|
|
2850
2987
|
open,
|
|
2851
2988
|
onOpenChange,
|
|
@@ -2862,7 +2999,10 @@ function LoadMessageDialog({
|
|
|
2862
2999
|
const [channelId, setChannelId] = React.useState("");
|
|
2863
3000
|
const [recent, setRecent] = React.useState(null);
|
|
2864
3001
|
const [recentError, setRecentError] = React.useState(null);
|
|
3002
|
+
const [selectedRecent, setSelectedRecent] = React.useState(null);
|
|
3003
|
+
const [signInPolling, setSignInPolling] = React.useState(false);
|
|
2865
3004
|
const inputRef = React.useRef(null);
|
|
3005
|
+
const signInPollRef = React.useRef(null);
|
|
2866
3006
|
const onLoadMessageRef = React.useRef(onLoadMessage);
|
|
2867
3007
|
const loadRecentMessagesRef = React.useRef(loadRecentMessages);
|
|
2868
3008
|
const loadChannelsRef = React.useRef(loadChannels);
|
|
@@ -2881,6 +3021,7 @@ function LoadMessageDialog({
|
|
|
2881
3021
|
setChannelId("");
|
|
2882
3022
|
setRecent(null);
|
|
2883
3023
|
setRecentError(null);
|
|
3024
|
+
setSelectedRecent(null);
|
|
2884
3025
|
if (!loadRecentMessagesRef.current) {
|
|
2885
3026
|
setChannels([]);
|
|
2886
3027
|
setChannelsError(null);
|
|
@@ -2908,6 +3049,7 @@ function LoadMessageDialog({
|
|
|
2908
3049
|
}
|
|
2909
3050
|
setRecent(null);
|
|
2910
3051
|
setRecentError(null);
|
|
3052
|
+
setSelectedRecent(null);
|
|
2911
3053
|
let cancelled = false;
|
|
2912
3054
|
loadRecentMessagesRef.current(channelId).then((list) => {
|
|
2913
3055
|
if (!cancelled) {
|
|
@@ -2922,7 +3064,25 @@ function LoadMessageDialog({
|
|
|
2922
3064
|
cancelled = true;
|
|
2923
3065
|
};
|
|
2924
3066
|
}, [open, channelId]);
|
|
3067
|
+
const stopSignInPoll = React.useCallback(() => {
|
|
3068
|
+
if (signInPollRef.current) {
|
|
3069
|
+
clearInterval(signInPollRef.current);
|
|
3070
|
+
signInPollRef.current = null;
|
|
3071
|
+
}
|
|
3072
|
+
setSignInPolling(false);
|
|
3073
|
+
}, []);
|
|
3074
|
+
React.useEffect(() => {
|
|
3075
|
+
if (!open) {
|
|
3076
|
+
stopSignInPoll();
|
|
3077
|
+
}
|
|
3078
|
+
return stopSignInPoll;
|
|
3079
|
+
}, [open, stopSignInPoll]);
|
|
2925
3080
|
const handleLoad = async () => {
|
|
3081
|
+
stopSignInPoll();
|
|
3082
|
+
if (selectedRecent) {
|
|
3083
|
+
onLoaded(recentToResult(selectedRecent));
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
2926
3086
|
const trimmed = link.trim();
|
|
2927
3087
|
if (!trimmed) {
|
|
2928
3088
|
setStatus({ kind: "error", error: "Paste a Slack message link first." });
|
|
@@ -2935,12 +3095,42 @@ function LoadMessageDialog({
|
|
|
2935
3095
|
onLoaded(result);
|
|
2936
3096
|
return;
|
|
2937
3097
|
}
|
|
2938
|
-
setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks });
|
|
3098
|
+
setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks, oauthUrl: result.oauthUrl });
|
|
2939
3099
|
} catch (e) {
|
|
2940
3100
|
setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
|
|
2941
3101
|
}
|
|
2942
3102
|
};
|
|
3103
|
+
const startFindSignIn = (oauthUrl) => {
|
|
3104
|
+
if (!isSafeHref(oauthUrl)) {
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
const trimmed = link.trim();
|
|
3108
|
+
if (!trimmed) {
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
window.open(oauthUrl, "_blank", "noopener,noreferrer");
|
|
3112
|
+
stopSignInPoll();
|
|
3113
|
+
setSignInPolling(true);
|
|
3114
|
+
let tries = 0;
|
|
3115
|
+
const POLL_INTERVAL_MS2 = 2500;
|
|
3116
|
+
const MAX_POLLS2 = 24;
|
|
3117
|
+
signInPollRef.current = setInterval(() => {
|
|
3118
|
+
tries += 1;
|
|
3119
|
+
onLoadMessageRef.current({ link: trimmed }).then((result) => {
|
|
3120
|
+
if (result.ok) {
|
|
3121
|
+
stopSignInPoll();
|
|
3122
|
+
onLoaded(result);
|
|
3123
|
+
}
|
|
3124
|
+
}).catch(() => {
|
|
3125
|
+
}).finally(() => {
|
|
3126
|
+
if (tries >= MAX_POLLS2) {
|
|
3127
|
+
stopSignInPoll();
|
|
3128
|
+
}
|
|
3129
|
+
});
|
|
3130
|
+
}, POLL_INTERVAL_MS2);
|
|
3131
|
+
};
|
|
2943
3132
|
const notEditable = status.kind === "not-editable" ? status : null;
|
|
3133
|
+
const signInUrl = notEditable?.oauthUrl && isSafeHref(notEditable.oauthUrl) ? notEditable.oauthUrl : null;
|
|
2944
3134
|
return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2945
3135
|
DialogContent,
|
|
2946
3136
|
{
|
|
@@ -2951,7 +3141,7 @@ function LoadMessageDialog({
|
|
|
2951
3141
|
},
|
|
2952
3142
|
children: [
|
|
2953
3143
|
/* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
|
|
2954
|
-
/* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "
|
|
3144
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Find an existing message" }),
|
|
2955
3145
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-1.5 text-left", children: [
|
|
2956
3146
|
/* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: hasRecent ? "Paste a Slack message link, or pick a recent message your app posted." : `Paste a Slack message link (Slack's "Copy link") to load its blocks for editing.` }),
|
|
2957
3147
|
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
@@ -2985,6 +3175,8 @@ function LoadMessageDialog({
|
|
|
2985
3175
|
value: link,
|
|
2986
3176
|
onChange: (e) => {
|
|
2987
3177
|
setLink(e.target.value);
|
|
3178
|
+
setSelectedRecent(null);
|
|
3179
|
+
stopSignInPoll();
|
|
2988
3180
|
if (status.kind !== "idle" && status.kind !== "loading") {
|
|
2989
3181
|
setStatus({ kind: "idle" });
|
|
2990
3182
|
}
|
|
@@ -3007,7 +3199,8 @@ function LoadMessageDialog({
|
|
|
3007
3199
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
3008
3200
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: notEditable.reason })
|
|
3009
3201
|
] }),
|
|
3010
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3202
|
+
signInUrl && /* @__PURE__ */ jsxRuntime.jsx(SlackSignInButton, { onClick: () => startFindSignIn(signInUrl), polling: signInPolling }),
|
|
3203
|
+
notEditable.blocks && notEditable.blocks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3011
3204
|
Button,
|
|
3012
3205
|
{
|
|
3013
3206
|
type: "button",
|
|
@@ -3053,26 +3246,52 @@ function LoadMessageDialog({
|
|
|
3053
3246
|
channelId && recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No recent messages from this app in this channel." }),
|
|
3054
3247
|
channelId && recent && recent.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-48 min-w-0 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
|
|
3055
3248
|
const asUser = (m.editableVia ?? "bot") === "user";
|
|
3249
|
+
const authorName = m.username || (asUser ? "You" : "App bot");
|
|
3250
|
+
const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
|
|
3251
|
+
const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
|
|
3056
3252
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3057
3253
|
"button",
|
|
3058
3254
|
{
|
|
3059
3255
|
type: "button",
|
|
3060
|
-
|
|
3061
|
-
|
|
3256
|
+
"aria-pressed": isSelected,
|
|
3257
|
+
onClick: () => {
|
|
3258
|
+
setSelectedRecent(m);
|
|
3259
|
+
setLink("");
|
|
3260
|
+
if (status.kind !== "idle" && status.kind !== "loading") {
|
|
3261
|
+
setStatus({ kind: "idle" });
|
|
3262
|
+
}
|
|
3263
|
+
},
|
|
3264
|
+
className: cn(
|
|
3265
|
+
// Inset rings so the scroll container's overflow clip
|
|
3266
|
+
// can't shave the corners off the selected outline.
|
|
3267
|
+
"flex min-w-0 items-start gap-2.5 rounded-md border border-input bg-background px-3 py-2 text-left hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
3268
|
+
isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
|
|
3269
|
+
),
|
|
3062
3270
|
children: [
|
|
3063
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3271
|
+
safeIcon ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3272
|
+
"span",
|
|
3273
|
+
{
|
|
3274
|
+
"aria-hidden": true,
|
|
3275
|
+
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
|
|
3276
|
+
children: authorName.charAt(0).toUpperCase()
|
|
3277
|
+
}
|
|
3278
|
+
),
|
|
3279
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3280
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
3281
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
|
|
3282
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded border px-1 py-px text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: asUser ? "You" : "Bot" })
|
|
3283
|
+
] }),
|
|
3284
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
3285
|
+
new Date(Number(m.ts) * 1e3).toLocaleString(),
|
|
3067
3286
|
" ",
|
|
3068
3287
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
|
|
3069
3288
|
"(",
|
|
3070
3289
|
m.ts,
|
|
3071
3290
|
")"
|
|
3072
3291
|
] })
|
|
3073
|
-
] })
|
|
3074
|
-
|
|
3075
|
-
|
|
3292
|
+
] }),
|
|
3293
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
|
|
3294
|
+
] })
|
|
3076
3295
|
]
|
|
3077
3296
|
},
|
|
3078
3297
|
`${m.channelId}:${m.ts}`
|
|
@@ -3082,7 +3301,15 @@ function LoadMessageDialog({
|
|
|
3082
3301
|
] }),
|
|
3083
3302
|
/* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
|
|
3084
3303
|
/* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
|
|
3085
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3304
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3305
|
+
Button,
|
|
3306
|
+
{
|
|
3307
|
+
type: "button",
|
|
3308
|
+
onClick: handleLoad,
|
|
3309
|
+
disabled: status.kind === "loading" || !selectedRecent && !link.trim(),
|
|
3310
|
+
children: status.kind === "loading" ? "Loading\u2026" : "Find message"
|
|
3311
|
+
}
|
|
3312
|
+
)
|
|
3086
3313
|
] })
|
|
3087
3314
|
]
|
|
3088
3315
|
}
|
|
@@ -3301,18 +3528,13 @@ function SendDialog({
|
|
|
3301
3528
|
const [channels, setChannels] = React.useState(null);
|
|
3302
3529
|
const [channelsError, setChannelsError] = React.useState(null);
|
|
3303
3530
|
const [channelId, setChannelId] = React.useState("");
|
|
3304
|
-
const [userStatus, setUserStatus] = React.useState(null);
|
|
3305
3531
|
const [sendAs, setSendAs] = React.useState("bot");
|
|
3306
3532
|
const [status, setStatus] = React.useState({ kind: "idle" });
|
|
3533
|
+
const { userStatus, polling, startSignIn } = useSlackSignIn(loadSendAsUserStatus, { open, enabled: true });
|
|
3307
3534
|
const loadChannelsRef = React.useRef(loadChannels);
|
|
3308
|
-
const loadSendAsUserStatusRef = React.useRef(loadSendAsUserStatus);
|
|
3309
3535
|
React.useEffect(() => {
|
|
3310
3536
|
loadChannelsRef.current = loadChannels;
|
|
3311
|
-
loadSendAsUserStatusRef.current = loadSendAsUserStatus;
|
|
3312
3537
|
});
|
|
3313
|
-
const refreshSendAsUser = React.useCallback(() => {
|
|
3314
|
-
loadSendAsUserStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
|
|
3315
|
-
}, []);
|
|
3316
3538
|
React.useEffect(() => {
|
|
3317
3539
|
if (!open) {
|
|
3318
3540
|
return;
|
|
@@ -3320,7 +3542,6 @@ function SendDialog({
|
|
|
3320
3542
|
setStatus({ kind: "idle" });
|
|
3321
3543
|
setChannels(null);
|
|
3322
3544
|
setChannelsError(null);
|
|
3323
|
-
setUserStatus(null);
|
|
3324
3545
|
setChannelId("");
|
|
3325
3546
|
setSendAs("bot");
|
|
3326
3547
|
let cancelled = false;
|
|
@@ -3336,19 +3557,10 @@ function SendDialog({
|
|
|
3336
3557
|
}
|
|
3337
3558
|
setChannelsError(e instanceof Error ? e.message : "Failed to load channels");
|
|
3338
3559
|
});
|
|
3339
|
-
refreshSendAsUser();
|
|
3340
3560
|
return () => {
|
|
3341
3561
|
cancelled = true;
|
|
3342
3562
|
};
|
|
3343
|
-
}, [open
|
|
3344
|
-
React.useEffect(() => {
|
|
3345
|
-
if (!open) {
|
|
3346
|
-
return;
|
|
3347
|
-
}
|
|
3348
|
-
const handler = () => refreshSendAsUser();
|
|
3349
|
-
window.addEventListener("focus", handler);
|
|
3350
|
-
return () => window.removeEventListener("focus", handler);
|
|
3351
|
-
}, [open, refreshSendAsUser]);
|
|
3563
|
+
}, [open]);
|
|
3352
3564
|
const handleSubmit = async () => {
|
|
3353
3565
|
if (!channelId) {
|
|
3354
3566
|
setStatus({ kind: "error", error: "Please pick a channel." });
|
|
@@ -3402,22 +3614,9 @@ function SendDialog({
|
|
|
3402
3614
|
]
|
|
3403
3615
|
}
|
|
3404
3616
|
),
|
|
3405
|
-
userStatus && !userStatus.canSendAsUser && userStatus.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3406
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3407
|
-
|
|
3408
|
-
{
|
|
3409
|
-
href: userStatus.oauthUrl,
|
|
3410
|
-
target: "_blank",
|
|
3411
|
-
rel: "noopener noreferrer",
|
|
3412
|
-
className: "inline-flex items-center gap-1 text-primary hover:underline",
|
|
3413
|
-
children: [
|
|
3414
|
-
"Sign in with Slack ",
|
|
3415
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
|
|
3416
|
-
]
|
|
3417
|
-
}
|
|
3418
|
-
),
|
|
3419
|
-
" ",
|
|
3420
|
-
"to post as yourself."
|
|
3617
|
+
userStatus && !userStatus.canSendAsUser && userStatus.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3618
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Connect your Slack account to post as yourself." }),
|
|
3619
|
+
/* @__PURE__ */ jsxRuntime.jsx(SlackSignInButton, { onClick: startSignIn, polling })
|
|
3421
3620
|
] })
|
|
3422
3621
|
] }),
|
|
3423
3622
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
@@ -9191,7 +9390,7 @@ function Toolbar2({
|
|
|
9191
9390
|
onOpenLoad,
|
|
9192
9391
|
onOpenUpdate,
|
|
9193
9392
|
onExitEdit,
|
|
9194
|
-
loadButtonLabel = "
|
|
9393
|
+
loadButtonLabel = "Find message",
|
|
9195
9394
|
updateButtonLabel = "Review & update"
|
|
9196
9395
|
}) {
|
|
9197
9396
|
const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
|
|
@@ -9475,33 +9674,13 @@ function UpdateDialog({
|
|
|
9475
9674
|
onShowIssues
|
|
9476
9675
|
}) {
|
|
9477
9676
|
const asUser = target.editableVia === "user";
|
|
9478
|
-
const [userStatus, setUserStatus] = React.useState(null);
|
|
9479
9677
|
const [status, setStatus] = React.useState({ kind: "idle" });
|
|
9480
|
-
const
|
|
9678
|
+
const { userStatus, polling, startSignIn } = useSlackSignIn(loadSendAsUserStatus, { open, enabled: asUser });
|
|
9481
9679
|
React.useEffect(() => {
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
const refreshSendAsUser = React.useCallback(() => {
|
|
9485
|
-
loadSendAsUserStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
|
|
9486
|
-
}, []);
|
|
9487
|
-
React.useEffect(() => {
|
|
9488
|
-
if (!open) {
|
|
9489
|
-
return;
|
|
9490
|
-
}
|
|
9491
|
-
setStatus({ kind: "idle" });
|
|
9492
|
-
if (asUser) {
|
|
9493
|
-
setUserStatus(null);
|
|
9494
|
-
refreshSendAsUser();
|
|
9495
|
-
}
|
|
9496
|
-
}, [open, asUser, refreshSendAsUser]);
|
|
9497
|
-
React.useEffect(() => {
|
|
9498
|
-
if (!open || !asUser) {
|
|
9499
|
-
return;
|
|
9680
|
+
if (open) {
|
|
9681
|
+
setStatus({ kind: "idle" });
|
|
9500
9682
|
}
|
|
9501
|
-
|
|
9502
|
-
window.addEventListener("focus", handler);
|
|
9503
|
-
return () => window.removeEventListener("focus", handler);
|
|
9504
|
-
}, [open, asUser, refreshSendAsUser]);
|
|
9683
|
+
}, [open]);
|
|
9505
9684
|
const needsSignIn = asUser && userStatus !== null && !userStatus.canSendAsUser;
|
|
9506
9685
|
const handleSubmit = async () => {
|
|
9507
9686
|
setStatus({ kind: "updating" });
|
|
@@ -9536,22 +9715,9 @@ function UpdateDialog({
|
|
|
9536
9715
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
9537
9716
|
/* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Posting as" }),
|
|
9538
9717
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex h-9 items-center rounded-md border border-input bg-muted px-3 text-sm text-muted-foreground", children: asUser ? "Your account" : "App bot" }),
|
|
9539
|
-
needsSignIn && userStatus?.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9540
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9541
|
-
|
|
9542
|
-
{
|
|
9543
|
-
href: userStatus.oauthUrl,
|
|
9544
|
-
target: "_blank",
|
|
9545
|
-
rel: "noopener noreferrer",
|
|
9546
|
-
className: "inline-flex items-center gap-1 text-primary hover:underline",
|
|
9547
|
-
children: [
|
|
9548
|
-
"Sign in with Slack ",
|
|
9549
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
|
|
9550
|
-
]
|
|
9551
|
-
}
|
|
9552
|
-
),
|
|
9553
|
-
" ",
|
|
9554
|
-
"to update your own message."
|
|
9718
|
+
needsSignIn && userStatus?.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9719
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Connect your Slack account to edit your own messages." }),
|
|
9720
|
+
/* @__PURE__ */ jsxRuntime.jsx(SlackSignInButton, { onClick: startSignIn, polling })
|
|
9555
9721
|
] }),
|
|
9556
9722
|
needsSignIn && !userStatus?.oauthUrl && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Sign in with Slack to update your own message." })
|
|
9557
9723
|
] }),
|