@tightknitai/block-kitchen 0.9.3 → 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.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.cjs +232 -88
- 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 +233 -89
- 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.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { B as BlockKitchenProps, $ as Template, K as PreviewSurface, N as PreviewTheme } from './palette-
|
|
3
|
-
export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as CartesianChart, h as ChannelOption, i as Chart, j as ChartAxisConfig, k as ChartDataPoint, l as ChartSeries, m as ChartType, n as ContainerBlock, o as ContainerChildBlock, p as ContextActionsBlock, q as ContextActionsElement, r as CustomEmoji, D as DataVisualizationBlock, E as EditableVia, s as EditingConfig, F as FeedbackButtonSubobject, t as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, u as IconButtonIcon, v as InputBlock, L as LoadMessageInput, w as LoadResult, M as MarkdownBlock, P as PaletteSection, x as PaletteVariant, y as PieChart, z as PieChartSegment, G as PlanBlock, J as PreviewHooks, R as RecentMessage, S as SendAsUserStatus, O as SendPayload, Q as SendResult, T as SupportedBlock, U as SupportedBlockType, V as SupportedHeaderBlock, W as TableBlock, X as TableCell, Y as TableColumnSetting, Z as TaskCardBlock, _ as TaskCardStatus, a0 as UpdatePayload, a1 as UpdateResult, a2 as UrlSourceElement, a3 as VideoBlock, a5 as defaultPalette, a6 as extraAlertVariant, a7 as legacyInputVariants } from './palette-
|
|
2
|
+
import { B as BlockKitchenProps, $ as Template, K as PreviewSurface, N as PreviewTheme } from './palette-inX8F01o.cjs';
|
|
3
|
+
export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as CartesianChart, h as ChannelOption, i as Chart, j as ChartAxisConfig, k as ChartDataPoint, l as ChartSeries, m as ChartType, n as ContainerBlock, o as ContainerChildBlock, p as ContextActionsBlock, q as ContextActionsElement, r as CustomEmoji, D as DataVisualizationBlock, E as EditableVia, s as EditingConfig, F as FeedbackButtonSubobject, t as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, u as IconButtonIcon, v as InputBlock, L as LoadMessageInput, w as LoadResult, M as MarkdownBlock, P as PaletteSection, x as PaletteVariant, y as PieChart, z as PieChartSegment, G as PlanBlock, J as PreviewHooks, R as RecentMessage, S as SendAsUserStatus, O as SendPayload, Q as SendResult, T as SupportedBlock, U as SupportedBlockType, V as SupportedHeaderBlock, W as TableBlock, X as TableCell, Y as TableColumnSetting, Z as TaskCardBlock, _ as TaskCardStatus, a0 as UpdatePayload, a1 as UpdateResult, a2 as UrlSourceElement, a3 as VideoBlock, a5 as defaultPalette, a6 as extraAlertVariant, a7 as legacyInputVariants } from './palette-inX8F01o.cjs';
|
|
4
4
|
export { decodeBlocksFromString, encodeBlocksToString, toSlackBlocks } from './helpers.cjs';
|
|
5
5
|
import 'slack-web-api-client';
|
|
6
6
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { B as BlockKitchenProps, $ as Template, K as PreviewSurface, N as PreviewTheme } from './palette-
|
|
3
|
-
export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as CartesianChart, h as ChannelOption, i as Chart, j as ChartAxisConfig, k as ChartDataPoint, l as ChartSeries, m as ChartType, n as ContainerBlock, o as ContainerChildBlock, p as ContextActionsBlock, q as ContextActionsElement, r as CustomEmoji, D as DataVisualizationBlock, E as EditableVia, s as EditingConfig, F as FeedbackButtonSubobject, t as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, u as IconButtonIcon, v as InputBlock, L as LoadMessageInput, w as LoadResult, M as MarkdownBlock, P as PaletteSection, x as PaletteVariant, y as PieChart, z as PieChartSegment, G as PlanBlock, J as PreviewHooks, R as RecentMessage, S as SendAsUserStatus, O as SendPayload, Q as SendResult, T as SupportedBlock, U as SupportedBlockType, V as SupportedHeaderBlock, W as TableBlock, X as TableCell, Y as TableColumnSetting, Z as TaskCardBlock, _ as TaskCardStatus, a0 as UpdatePayload, a1 as UpdateResult, a2 as UrlSourceElement, a3 as VideoBlock, a5 as defaultPalette, a6 as extraAlertVariant, a7 as legacyInputVariants } from './palette-
|
|
2
|
+
import { B as BlockKitchenProps, $ as Template, K as PreviewSurface, N as PreviewTheme } from './palette-inX8F01o.js';
|
|
3
|
+
export { A as AlertBlock, a as AlertLevel, b as BrandPreset, c as BrandTheme, d as BrandTokens, e as BuilderBlock, C as CardBlock, f as CarouselBlock, g as CartesianChart, h as ChannelOption, i as Chart, j as ChartAxisConfig, k as ChartDataPoint, l as ChartSeries, m as ChartType, n as ContainerBlock, o as ContainerChildBlock, p as ContextActionsBlock, q as ContextActionsElement, r as CustomEmoji, D as DataVisualizationBlock, E as EditableVia, s as EditingConfig, F as FeedbackButtonSubobject, t as FeedbackButtonsElement, H as HeaderLevel, I as IconButtonElement, u as IconButtonIcon, v as InputBlock, L as LoadMessageInput, w as LoadResult, M as MarkdownBlock, P as PaletteSection, x as PaletteVariant, y as PieChart, z as PieChartSegment, G as PlanBlock, J as PreviewHooks, R as RecentMessage, S as SendAsUserStatus, O as SendPayload, Q as SendResult, T as SupportedBlock, U as SupportedBlockType, V as SupportedHeaderBlock, W as TableBlock, X as TableCell, Y as TableColumnSetting, Z as TaskCardBlock, _ as TaskCardStatus, a0 as UpdatePayload, a1 as UpdateResult, a2 as UrlSourceElement, a3 as VideoBlock, a5 as defaultPalette, a6 as extraAlertVariant, a7 as legacyInputVariants } from './palette-inX8F01o.js';
|
|
4
4
|
export { decodeBlocksFromString, encodeBlocksToString, toSlackBlocks } from './helpers.js';
|
|
5
5
|
import 'slack-web-api-client';
|
|
6
6
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useSensors, useSensor, PointerSensor, TouchSensor, KeyboardSensor, pointerWithin, closestCenter, DndContext, DragOverlay, useDroppable, useDraggable } from '@dnd-kit/core';
|
|
2
2
|
import { sortableKeyboardCoordinates, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
|
3
|
-
import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, MailSearch, Plus, ChevronDown, BookOpen, ExternalLink, AlertTriangle, Trash2, Code2, Pencil, Send, Search, Info, CheckCircle2,
|
|
3
|
+
import { X, Circle, Sparkles, FileText, AlignLeft, MousePointerClick, TextCursorInput, LayoutTemplate, Bell, Pilcrow, Image, Video, GalleryHorizontal, Table, ChartColumnBig, Container, GripVertical, Sun, Moon, MessageSquare, AppWindow, Home, MailSearch, Plus, ChevronDown, BookOpen, ExternalLink, AlertTriangle, Trash2, Code2, Pencil, Send, Search, Check, Copy, Info, CheckCircle2, ChevronRight, LayoutGrid, ArrowUp, ArrowDown, Loader2, Type, Bold, Italic, Strikethrough, Underline, Code, List, ListOrdered, Quote, Undo2, Redo2, Smile, Link as Link$1 } from 'lucide-react';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { createContext, useMemo, useState, useCallback, useRef, useEffect, useId, useContext } from 'react';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -2617,8 +2617,10 @@ function JsonDrawer({
|
|
|
2617
2617
|
const [value, setValue] = useState("");
|
|
2618
2618
|
const [parseError, setParseError] = useState(null);
|
|
2619
2619
|
const [validationErrors, setValidationErrors] = useState([]);
|
|
2620
|
+
const [copied, setCopied] = useState(false);
|
|
2620
2621
|
const textareaRef = useRef(null);
|
|
2621
2622
|
const gutterRef = useRef(null);
|
|
2623
|
+
const copyResetRef = useRef(null);
|
|
2622
2624
|
const blocksRef = useRef(blocks);
|
|
2623
2625
|
useEffect(() => {
|
|
2624
2626
|
blocksRef.current = blocks;
|
|
@@ -2628,8 +2630,27 @@ function JsonDrawer({
|
|
|
2628
2630
|
setValue(JSON.stringify(blocksRef.current, null, 2));
|
|
2629
2631
|
setParseError(null);
|
|
2630
2632
|
setValidationErrors([]);
|
|
2633
|
+
setCopied(false);
|
|
2631
2634
|
}
|
|
2632
2635
|
}, [open]);
|
|
2636
|
+
useEffect(
|
|
2637
|
+
() => () => {
|
|
2638
|
+
if (copyResetRef.current) {
|
|
2639
|
+
clearTimeout(copyResetRef.current);
|
|
2640
|
+
}
|
|
2641
|
+
},
|
|
2642
|
+
[]
|
|
2643
|
+
);
|
|
2644
|
+
const handleCopy = () => {
|
|
2645
|
+
navigator.clipboard?.writeText(value).then(() => {
|
|
2646
|
+
setCopied(true);
|
|
2647
|
+
if (copyResetRef.current) {
|
|
2648
|
+
clearTimeout(copyResetRef.current);
|
|
2649
|
+
}
|
|
2650
|
+
copyResetRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
2651
|
+
}).catch(() => {
|
|
2652
|
+
});
|
|
2653
|
+
};
|
|
2633
2654
|
const handleChange = (next) => {
|
|
2634
2655
|
setValue(next);
|
|
2635
2656
|
if (next.length > MAX_JSON_BYTES) {
|
|
@@ -2676,7 +2697,17 @@ function JsonDrawer({
|
|
|
2676
2697
|
/* @__PURE__ */ jsx(SheetTitle, { children: "Block Kit JSON" }),
|
|
2677
2698
|
/* @__PURE__ */ jsx(SheetDescription, { children: "Edits update the preview as you type. Parse errors show below." })
|
|
2678
2699
|
] }),
|
|
2679
|
-
/* @__PURE__ */ 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: [
|
|
2700
|
+
/* @__PURE__ */ 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: [
|
|
2701
|
+
/* @__PURE__ */ jsx(
|
|
2702
|
+
"button",
|
|
2703
|
+
{
|
|
2704
|
+
type: "button",
|
|
2705
|
+
onClick: handleCopy,
|
|
2706
|
+
"aria-label": copied ? "Copied to clipboard" : "Copy JSON",
|
|
2707
|
+
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",
|
|
2708
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-emerald-600" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
|
|
2709
|
+
}
|
|
2710
|
+
),
|
|
2680
2711
|
/* @__PURE__ */ jsx(
|
|
2681
2712
|
"div",
|
|
2682
2713
|
{
|
|
@@ -2825,6 +2856,79 @@ var Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ j
|
|
|
2825
2856
|
}
|
|
2826
2857
|
));
|
|
2827
2858
|
Label.displayName = LabelPrimitive.Root.displayName;
|
|
2859
|
+
var POLL_INTERVAL_MS = 2500;
|
|
2860
|
+
var MAX_POLLS = 24;
|
|
2861
|
+
function useSlackSignIn(loadStatus, { open, enabled }) {
|
|
2862
|
+
const [userStatus, setUserStatus] = useState(null);
|
|
2863
|
+
const [polling, setPolling] = useState(false);
|
|
2864
|
+
const loadStatusRef = useRef(loadStatus);
|
|
2865
|
+
useEffect(() => {
|
|
2866
|
+
loadStatusRef.current = loadStatus;
|
|
2867
|
+
});
|
|
2868
|
+
const refresh = useCallback(() => {
|
|
2869
|
+
loadStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
|
|
2870
|
+
}, []);
|
|
2871
|
+
const pollRef = useRef(null);
|
|
2872
|
+
const stopPolling = useCallback(() => {
|
|
2873
|
+
if (pollRef.current) {
|
|
2874
|
+
clearInterval(pollRef.current);
|
|
2875
|
+
pollRef.current = null;
|
|
2876
|
+
}
|
|
2877
|
+
setPolling(false);
|
|
2878
|
+
}, []);
|
|
2879
|
+
const startSignIn = useCallback(() => {
|
|
2880
|
+
const url = userStatus?.oauthUrl;
|
|
2881
|
+
if (!url || !isSafeHref(url)) {
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
2885
|
+
stopPolling();
|
|
2886
|
+
setPolling(true);
|
|
2887
|
+
let tries = 0;
|
|
2888
|
+
pollRef.current = setInterval(() => {
|
|
2889
|
+
tries += 1;
|
|
2890
|
+
loadStatusRef.current().then((next) => {
|
|
2891
|
+
setUserStatus(next);
|
|
2892
|
+
if (next.canSendAsUser) {
|
|
2893
|
+
stopPolling();
|
|
2894
|
+
}
|
|
2895
|
+
}).catch(() => {
|
|
2896
|
+
}).finally(() => {
|
|
2897
|
+
if (tries >= MAX_POLLS) {
|
|
2898
|
+
stopPolling();
|
|
2899
|
+
}
|
|
2900
|
+
});
|
|
2901
|
+
}, POLL_INTERVAL_MS);
|
|
2902
|
+
}, [userStatus, stopPolling]);
|
|
2903
|
+
useEffect(() => {
|
|
2904
|
+
if (!open || !enabled) {
|
|
2905
|
+
setUserStatus(null);
|
|
2906
|
+
stopPolling();
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
setUserStatus(null);
|
|
2910
|
+
refresh();
|
|
2911
|
+
return stopPolling;
|
|
2912
|
+
}, [open, enabled, refresh, stopPolling]);
|
|
2913
|
+
useEffect(() => {
|
|
2914
|
+
if (!open || !enabled) {
|
|
2915
|
+
return;
|
|
2916
|
+
}
|
|
2917
|
+
const handler = () => refresh();
|
|
2918
|
+
window.addEventListener("focus", handler);
|
|
2919
|
+
return () => window.removeEventListener("focus", handler);
|
|
2920
|
+
}, [open, enabled, refresh]);
|
|
2921
|
+
return { userStatus, polling, startSignIn };
|
|
2922
|
+
}
|
|
2923
|
+
function SlackSignInButton({ onClick, polling }) {
|
|
2924
|
+
return /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", className: "w-fit", onClick, disabled: polling, children: polling ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2925
|
+
/* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }),
|
|
2926
|
+
" Waiting for Slack\u2026"
|
|
2927
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2928
|
+
"Sign in with Slack ",
|
|
2929
|
+
/* @__PURE__ */ jsx(ExternalLink, {})
|
|
2930
|
+
] }) });
|
|
2931
|
+
}
|
|
2828
2932
|
function recentToResult(msg) {
|
|
2829
2933
|
return {
|
|
2830
2934
|
ok: true,
|
|
@@ -2838,6 +2942,17 @@ function recentToResult(msg) {
|
|
|
2838
2942
|
iconUrl: msg.iconUrl
|
|
2839
2943
|
};
|
|
2840
2944
|
}
|
|
2945
|
+
function previewText(m) {
|
|
2946
|
+
if (m.label) {
|
|
2947
|
+
return m.label;
|
|
2948
|
+
}
|
|
2949
|
+
for (const b of m.blocks) {
|
|
2950
|
+
if ((b.type === "header" || b.type === "section") && "text" in b && b.text && "text" in b.text) {
|
|
2951
|
+
return b.text.text;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return `${m.blocks.length} block${m.blocks.length === 1 ? "" : "s"}`;
|
|
2955
|
+
}
|
|
2841
2956
|
function LoadMessageDialog({
|
|
2842
2957
|
open,
|
|
2843
2958
|
onOpenChange,
|
|
@@ -2854,7 +2969,10 @@ function LoadMessageDialog({
|
|
|
2854
2969
|
const [channelId, setChannelId] = useState("");
|
|
2855
2970
|
const [recent, setRecent] = useState(null);
|
|
2856
2971
|
const [recentError, setRecentError] = useState(null);
|
|
2972
|
+
const [selectedRecent, setSelectedRecent] = useState(null);
|
|
2973
|
+
const [signInPolling, setSignInPolling] = useState(false);
|
|
2857
2974
|
const inputRef = useRef(null);
|
|
2975
|
+
const signInPollRef = useRef(null);
|
|
2858
2976
|
const onLoadMessageRef = useRef(onLoadMessage);
|
|
2859
2977
|
const loadRecentMessagesRef = useRef(loadRecentMessages);
|
|
2860
2978
|
const loadChannelsRef = useRef(loadChannels);
|
|
@@ -2873,6 +2991,7 @@ function LoadMessageDialog({
|
|
|
2873
2991
|
setChannelId("");
|
|
2874
2992
|
setRecent(null);
|
|
2875
2993
|
setRecentError(null);
|
|
2994
|
+
setSelectedRecent(null);
|
|
2876
2995
|
if (!loadRecentMessagesRef.current) {
|
|
2877
2996
|
setChannels([]);
|
|
2878
2997
|
setChannelsError(null);
|
|
@@ -2900,6 +3019,7 @@ function LoadMessageDialog({
|
|
|
2900
3019
|
}
|
|
2901
3020
|
setRecent(null);
|
|
2902
3021
|
setRecentError(null);
|
|
3022
|
+
setSelectedRecent(null);
|
|
2903
3023
|
let cancelled = false;
|
|
2904
3024
|
loadRecentMessagesRef.current(channelId).then((list) => {
|
|
2905
3025
|
if (!cancelled) {
|
|
@@ -2914,7 +3034,25 @@ function LoadMessageDialog({
|
|
|
2914
3034
|
cancelled = true;
|
|
2915
3035
|
};
|
|
2916
3036
|
}, [open, channelId]);
|
|
3037
|
+
const stopSignInPoll = useCallback(() => {
|
|
3038
|
+
if (signInPollRef.current) {
|
|
3039
|
+
clearInterval(signInPollRef.current);
|
|
3040
|
+
signInPollRef.current = null;
|
|
3041
|
+
}
|
|
3042
|
+
setSignInPolling(false);
|
|
3043
|
+
}, []);
|
|
3044
|
+
useEffect(() => {
|
|
3045
|
+
if (!open) {
|
|
3046
|
+
stopSignInPoll();
|
|
3047
|
+
}
|
|
3048
|
+
return stopSignInPoll;
|
|
3049
|
+
}, [open, stopSignInPoll]);
|
|
2917
3050
|
const handleLoad = async () => {
|
|
3051
|
+
stopSignInPoll();
|
|
3052
|
+
if (selectedRecent) {
|
|
3053
|
+
onLoaded(recentToResult(selectedRecent));
|
|
3054
|
+
return;
|
|
3055
|
+
}
|
|
2918
3056
|
const trimmed = link.trim();
|
|
2919
3057
|
if (!trimmed) {
|
|
2920
3058
|
setStatus({ kind: "error", error: "Paste a Slack message link first." });
|
|
@@ -2927,12 +3065,42 @@ function LoadMessageDialog({
|
|
|
2927
3065
|
onLoaded(result);
|
|
2928
3066
|
return;
|
|
2929
3067
|
}
|
|
2930
|
-
setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks });
|
|
3068
|
+
setStatus({ kind: "not-editable", reason: result.reason, blocks: result.blocks, oauthUrl: result.oauthUrl });
|
|
2931
3069
|
} catch (e) {
|
|
2932
3070
|
setStatus({ kind: "error", error: e instanceof Error ? e.message : "Failed to load message." });
|
|
2933
3071
|
}
|
|
2934
3072
|
};
|
|
3073
|
+
const startFindSignIn = (oauthUrl) => {
|
|
3074
|
+
if (!isSafeHref(oauthUrl)) {
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
const trimmed = link.trim();
|
|
3078
|
+
if (!trimmed) {
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
3081
|
+
window.open(oauthUrl, "_blank", "noopener,noreferrer");
|
|
3082
|
+
stopSignInPoll();
|
|
3083
|
+
setSignInPolling(true);
|
|
3084
|
+
let tries = 0;
|
|
3085
|
+
const POLL_INTERVAL_MS2 = 2500;
|
|
3086
|
+
const MAX_POLLS2 = 24;
|
|
3087
|
+
signInPollRef.current = setInterval(() => {
|
|
3088
|
+
tries += 1;
|
|
3089
|
+
onLoadMessageRef.current({ link: trimmed }).then((result) => {
|
|
3090
|
+
if (result.ok) {
|
|
3091
|
+
stopSignInPoll();
|
|
3092
|
+
onLoaded(result);
|
|
3093
|
+
}
|
|
3094
|
+
}).catch(() => {
|
|
3095
|
+
}).finally(() => {
|
|
3096
|
+
if (tries >= MAX_POLLS2) {
|
|
3097
|
+
stopSignInPoll();
|
|
3098
|
+
}
|
|
3099
|
+
});
|
|
3100
|
+
}, POLL_INTERVAL_MS2);
|
|
3101
|
+
};
|
|
2935
3102
|
const notEditable = status.kind === "not-editable" ? status : null;
|
|
3103
|
+
const signInUrl = notEditable?.oauthUrl && isSafeHref(notEditable.oauthUrl) ? notEditable.oauthUrl : null;
|
|
2936
3104
|
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
|
|
2937
3105
|
DialogContent,
|
|
2938
3106
|
{
|
|
@@ -2943,7 +3111,7 @@ function LoadMessageDialog({
|
|
|
2943
3111
|
},
|
|
2944
3112
|
children: [
|
|
2945
3113
|
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2946
|
-
/* @__PURE__ */ jsx(DialogTitle, { children: "
|
|
3114
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Find an existing message" }),
|
|
2947
3115
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5 text-left", children: [
|
|
2948
3116
|
/* @__PURE__ */ 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.` }),
|
|
2949
3117
|
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
@@ -2977,6 +3145,8 @@ function LoadMessageDialog({
|
|
|
2977
3145
|
value: link,
|
|
2978
3146
|
onChange: (e) => {
|
|
2979
3147
|
setLink(e.target.value);
|
|
3148
|
+
setSelectedRecent(null);
|
|
3149
|
+
stopSignInPoll();
|
|
2980
3150
|
if (status.kind !== "idle" && status.kind !== "loading") {
|
|
2981
3151
|
setStatus({ kind: "idle" });
|
|
2982
3152
|
}
|
|
@@ -2999,7 +3169,8 @@ function LoadMessageDialog({
|
|
|
2999
3169
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
|
|
3000
3170
|
/* @__PURE__ */ jsx("span", { className: "flex-1", children: notEditable.reason })
|
|
3001
3171
|
] }),
|
|
3002
|
-
/* @__PURE__ */ jsx(
|
|
3172
|
+
signInUrl && /* @__PURE__ */ jsx(SlackSignInButton, { onClick: () => startFindSignIn(signInUrl), polling: signInPolling }),
|
|
3173
|
+
notEditable.blocks && notEditable.blocks.length > 0 && /* @__PURE__ */ jsx(
|
|
3003
3174
|
Button,
|
|
3004
3175
|
{
|
|
3005
3176
|
type: "button",
|
|
@@ -3045,26 +3216,52 @@ function LoadMessageDialog({
|
|
|
3045
3216
|
channelId && recent && recent.length === 0 && !recentError && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No recent messages from this app in this channel." }),
|
|
3046
3217
|
channelId && recent && recent.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex max-h-48 min-w-0 flex-col gap-1 overflow-y-auto", children: recent.map((m) => {
|
|
3047
3218
|
const asUser = (m.editableVia ?? "bot") === "user";
|
|
3219
|
+
const authorName = m.username || (asUser ? "You" : "App bot");
|
|
3220
|
+
const safeIcon = isSafeImageSrc(m.iconUrl) ? m.iconUrl : void 0;
|
|
3221
|
+
const isSelected = selectedRecent?.channelId === m.channelId && selectedRecent?.ts === m.ts;
|
|
3048
3222
|
return /* @__PURE__ */ jsxs(
|
|
3049
3223
|
"button",
|
|
3050
3224
|
{
|
|
3051
3225
|
type: "button",
|
|
3052
|
-
|
|
3053
|
-
|
|
3226
|
+
"aria-pressed": isSelected,
|
|
3227
|
+
onClick: () => {
|
|
3228
|
+
setSelectedRecent(m);
|
|
3229
|
+
setLink("");
|
|
3230
|
+
if (status.kind !== "idle" && status.kind !== "loading") {
|
|
3231
|
+
setStatus({ kind: "idle" });
|
|
3232
|
+
}
|
|
3233
|
+
},
|
|
3234
|
+
className: cn(
|
|
3235
|
+
// Inset rings so the scroll container's overflow clip
|
|
3236
|
+
// can't shave the corners off the selected outline.
|
|
3237
|
+
"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",
|
|
3238
|
+
isSelected && "border-primary! bg-accent ring-1 ring-inset ring-primary"
|
|
3239
|
+
),
|
|
3054
3240
|
children: [
|
|
3055
|
-
/* @__PURE__ */
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3241
|
+
safeIcon ? /* @__PURE__ */ jsx("img", { src: safeIcon, alt: "", className: "h-9 w-9 shrink-0 rounded-md object-cover" }) : /* @__PURE__ */ jsx(
|
|
3242
|
+
"span",
|
|
3243
|
+
{
|
|
3244
|
+
"aria-hidden": true,
|
|
3245
|
+
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-muted text-sm font-semibold text-muted-foreground",
|
|
3246
|
+
children: authorName.charAt(0).toUpperCase()
|
|
3247
|
+
}
|
|
3248
|
+
),
|
|
3249
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3250
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
3251
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: authorName }),
|
|
3252
|
+
/* @__PURE__ */ 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" })
|
|
3253
|
+
] }),
|
|
3254
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
3255
|
+
new Date(Number(m.ts) * 1e3).toLocaleString(),
|
|
3059
3256
|
" ",
|
|
3060
3257
|
/* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
|
|
3061
3258
|
"(",
|
|
3062
3259
|
m.ts,
|
|
3063
3260
|
")"
|
|
3064
3261
|
] })
|
|
3065
|
-
] })
|
|
3066
|
-
|
|
3067
|
-
|
|
3262
|
+
] }),
|
|
3263
|
+
/* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-sm text-foreground", children: previewText(m) })
|
|
3264
|
+
] })
|
|
3068
3265
|
]
|
|
3069
3266
|
},
|
|
3070
3267
|
`${m.channelId}:${m.ts}`
|
|
@@ -3074,7 +3271,15 @@ function LoadMessageDialog({
|
|
|
3074
3271
|
] }),
|
|
3075
3272
|
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
3076
3273
|
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }),
|
|
3077
|
-
/* @__PURE__ */ jsx(
|
|
3274
|
+
/* @__PURE__ */ jsx(
|
|
3275
|
+
Button,
|
|
3276
|
+
{
|
|
3277
|
+
type: "button",
|
|
3278
|
+
onClick: handleLoad,
|
|
3279
|
+
disabled: status.kind === "loading" || !selectedRecent && !link.trim(),
|
|
3280
|
+
children: status.kind === "loading" ? "Loading\u2026" : "Find message"
|
|
3281
|
+
}
|
|
3282
|
+
)
|
|
3078
3283
|
] })
|
|
3079
3284
|
]
|
|
3080
3285
|
}
|
|
@@ -3293,18 +3498,13 @@ function SendDialog({
|
|
|
3293
3498
|
const [channels, setChannels] = useState(null);
|
|
3294
3499
|
const [channelsError, setChannelsError] = useState(null);
|
|
3295
3500
|
const [channelId, setChannelId] = useState("");
|
|
3296
|
-
const [userStatus, setUserStatus] = useState(null);
|
|
3297
3501
|
const [sendAs, setSendAs] = useState("bot");
|
|
3298
3502
|
const [status, setStatus] = useState({ kind: "idle" });
|
|
3503
|
+
const { userStatus, polling, startSignIn } = useSlackSignIn(loadSendAsUserStatus, { open, enabled: true });
|
|
3299
3504
|
const loadChannelsRef = useRef(loadChannels);
|
|
3300
|
-
const loadSendAsUserStatusRef = useRef(loadSendAsUserStatus);
|
|
3301
3505
|
useEffect(() => {
|
|
3302
3506
|
loadChannelsRef.current = loadChannels;
|
|
3303
|
-
loadSendAsUserStatusRef.current = loadSendAsUserStatus;
|
|
3304
3507
|
});
|
|
3305
|
-
const refreshSendAsUser = useCallback(() => {
|
|
3306
|
-
loadSendAsUserStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
|
|
3307
|
-
}, []);
|
|
3308
3508
|
useEffect(() => {
|
|
3309
3509
|
if (!open) {
|
|
3310
3510
|
return;
|
|
@@ -3312,7 +3512,6 @@ function SendDialog({
|
|
|
3312
3512
|
setStatus({ kind: "idle" });
|
|
3313
3513
|
setChannels(null);
|
|
3314
3514
|
setChannelsError(null);
|
|
3315
|
-
setUserStatus(null);
|
|
3316
3515
|
setChannelId("");
|
|
3317
3516
|
setSendAs("bot");
|
|
3318
3517
|
let cancelled = false;
|
|
@@ -3328,19 +3527,10 @@ function SendDialog({
|
|
|
3328
3527
|
}
|
|
3329
3528
|
setChannelsError(e instanceof Error ? e.message : "Failed to load channels");
|
|
3330
3529
|
});
|
|
3331
|
-
refreshSendAsUser();
|
|
3332
3530
|
return () => {
|
|
3333
3531
|
cancelled = true;
|
|
3334
3532
|
};
|
|
3335
|
-
}, [open
|
|
3336
|
-
useEffect(() => {
|
|
3337
|
-
if (!open) {
|
|
3338
|
-
return;
|
|
3339
|
-
}
|
|
3340
|
-
const handler = () => refreshSendAsUser();
|
|
3341
|
-
window.addEventListener("focus", handler);
|
|
3342
|
-
return () => window.removeEventListener("focus", handler);
|
|
3343
|
-
}, [open, refreshSendAsUser]);
|
|
3533
|
+
}, [open]);
|
|
3344
3534
|
const handleSubmit = async () => {
|
|
3345
3535
|
if (!channelId) {
|
|
3346
3536
|
setStatus({ kind: "error", error: "Please pick a channel." });
|
|
@@ -3394,22 +3584,9 @@ function SendDialog({
|
|
|
3394
3584
|
]
|
|
3395
3585
|
}
|
|
3396
3586
|
),
|
|
3397
|
-
userStatus && !userStatus.canSendAsUser && userStatus.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxs(
|
|
3398
|
-
/* @__PURE__ */
|
|
3399
|
-
|
|
3400
|
-
{
|
|
3401
|
-
href: userStatus.oauthUrl,
|
|
3402
|
-
target: "_blank",
|
|
3403
|
-
rel: "noopener noreferrer",
|
|
3404
|
-
className: "inline-flex items-center gap-1 text-primary hover:underline",
|
|
3405
|
-
children: [
|
|
3406
|
-
"Sign in with Slack ",
|
|
3407
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" })
|
|
3408
|
-
]
|
|
3409
|
-
}
|
|
3410
|
-
),
|
|
3411
|
-
" ",
|
|
3412
|
-
"to post as yourself."
|
|
3587
|
+
userStatus && !userStatus.canSendAsUser && userStatus.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3588
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Connect your Slack account to post as yourself." }),
|
|
3589
|
+
/* @__PURE__ */ jsx(SlackSignInButton, { onClick: startSignIn, polling })
|
|
3413
3590
|
] })
|
|
3414
3591
|
] }),
|
|
3415
3592
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
@@ -9183,7 +9360,7 @@ function Toolbar2({
|
|
|
9183
9360
|
onOpenLoad,
|
|
9184
9361
|
onOpenUpdate,
|
|
9185
9362
|
onExitEdit,
|
|
9186
|
-
loadButtonLabel = "
|
|
9363
|
+
loadButtonLabel = "Find message",
|
|
9187
9364
|
updateButtonLabel = "Review & update"
|
|
9188
9365
|
}) {
|
|
9189
9366
|
const activeTheme = THEME_OPTIONS.find((t) => t.value === previewTheme) ?? THEME_OPTIONS[0];
|
|
@@ -9467,33 +9644,13 @@ function UpdateDialog({
|
|
|
9467
9644
|
onShowIssues
|
|
9468
9645
|
}) {
|
|
9469
9646
|
const asUser = target.editableVia === "user";
|
|
9470
|
-
const [userStatus, setUserStatus] = useState(null);
|
|
9471
9647
|
const [status, setStatus] = useState({ kind: "idle" });
|
|
9472
|
-
const
|
|
9473
|
-
useEffect(() => {
|
|
9474
|
-
loadSendAsUserStatusRef.current = loadSendAsUserStatus;
|
|
9475
|
-
});
|
|
9476
|
-
const refreshSendAsUser = useCallback(() => {
|
|
9477
|
-
loadSendAsUserStatusRef.current().then(setUserStatus).catch(() => setUserStatus({ canSendAsUser: false }));
|
|
9478
|
-
}, []);
|
|
9648
|
+
const { userStatus, polling, startSignIn } = useSlackSignIn(loadSendAsUserStatus, { open, enabled: asUser });
|
|
9479
9649
|
useEffect(() => {
|
|
9480
|
-
if (
|
|
9481
|
-
|
|
9482
|
-
}
|
|
9483
|
-
setStatus({ kind: "idle" });
|
|
9484
|
-
if (asUser) {
|
|
9485
|
-
setUserStatus(null);
|
|
9486
|
-
refreshSendAsUser();
|
|
9487
|
-
}
|
|
9488
|
-
}, [open, asUser, refreshSendAsUser]);
|
|
9489
|
-
useEffect(() => {
|
|
9490
|
-
if (!open || !asUser) {
|
|
9491
|
-
return;
|
|
9650
|
+
if (open) {
|
|
9651
|
+
setStatus({ kind: "idle" });
|
|
9492
9652
|
}
|
|
9493
|
-
|
|
9494
|
-
window.addEventListener("focus", handler);
|
|
9495
|
-
return () => window.removeEventListener("focus", handler);
|
|
9496
|
-
}, [open, asUser, refreshSendAsUser]);
|
|
9653
|
+
}, [open]);
|
|
9497
9654
|
const needsSignIn = asUser && userStatus !== null && !userStatus.canSendAsUser;
|
|
9498
9655
|
const handleSubmit = async () => {
|
|
9499
9656
|
setStatus({ kind: "updating" });
|
|
@@ -9528,22 +9685,9 @@ function UpdateDialog({
|
|
|
9528
9685
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
9529
9686
|
/* @__PURE__ */ jsx(Label, { children: "Posting as" }),
|
|
9530
9687
|
/* @__PURE__ */ 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" }),
|
|
9531
|
-
needsSignIn && userStatus?.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxs(
|
|
9532
|
-
/* @__PURE__ */
|
|
9533
|
-
|
|
9534
|
-
{
|
|
9535
|
-
href: userStatus.oauthUrl,
|
|
9536
|
-
target: "_blank",
|
|
9537
|
-
rel: "noopener noreferrer",
|
|
9538
|
-
className: "inline-flex items-center gap-1 text-primary hover:underline",
|
|
9539
|
-
children: [
|
|
9540
|
-
"Sign in with Slack ",
|
|
9541
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3" })
|
|
9542
|
-
]
|
|
9543
|
-
}
|
|
9544
|
-
),
|
|
9545
|
-
" ",
|
|
9546
|
-
"to update your own message."
|
|
9688
|
+
needsSignIn && userStatus?.oauthUrl && isSafeHref(userStatus.oauthUrl) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9689
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Connect your Slack account to edit your own messages." }),
|
|
9690
|
+
/* @__PURE__ */ jsx(SlackSignInButton, { onClick: startSignIn, polling })
|
|
9547
9691
|
] }),
|
|
9548
9692
|
needsSignIn && !userStatus?.oauthUrl && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Sign in with Slack to update your own message." })
|
|
9549
9693
|
] }),
|