Skip to content

Commit addf2c9

Browse files
committed
Add Slack field note drafting modal
1 parent a362f05 commit addf2c9

5 files changed

Lines changed: 273 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Until those variables exist, API routes return a setup `503`.
8787

8888
## Slack Beta Setup
8989

90-
The Slack app is a free beta entry point for private dumps. It supports `/mumbl [text]`, `/mumbl room [team name]`, a `save_to_mumbl` message shortcut, and a private App Home. It does not read channel history.
90+
The Slack app is a free beta entry point for private dumps. It supports `/mumbl [text]`, `/mumbl room [team name]`, a `save_to_mumbl` message shortcut, App Home private dumping, and App Home field-note drafting from recent private dumps. It does not read channel history.
9191

9292
In Slack app settings:
9393

@@ -100,7 +100,7 @@ In Slack app settings:
100100

101101
Set `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET`, `SLACK_SIGNING_SECRET`, and `MUMBL_SLACK_TOKEN_ENCRYPTION_KEY` in the deployment environment. Install through `/api/slack/install`.
102102

103-
`/mumbl room platform team` creates a Mumbl room from Slack and returns a one-time creator handoff link for opening the room in a browser. `/mumbl start platform team` remains an alias. Optional team-read Slack posting is creator-enabled per room. If a creator switches it on, Mumbl starts an optional Slack permission upgrade that asks for `chat:write` and `groups:write` so it can create one private channel and post published team reads there. It still does not request Slack history scopes.
103+
`/mumbl room platform team` creates a Mumbl room from Slack and returns a one-time creator handoff link for opening the room in a browser. `/mumbl start platform team` remains an alias. App Home can draft a private field note from selected recent dumps, but review and publishing still happen in Mumbl. Optional team-read Slack posting is creator-enabled per room. If a creator switches it on, Mumbl starts an optional Slack permission upgrade that asks for `chat:write` and `groups:write` so it can create one private channel and post published team reads there. It still does not request Slack history scopes.
104104

105105
Slack reminders are intentionally not part of the beta because frequent scheduling does not fit the current free-tier posture.
106106

app/api/slack/interactions/route.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@ import {
1212
parseVerifiedSlackForm,
1313
postSlackResponse,
1414
saveSlackDump,
15+
createSlackFieldNoteDraft,
1516
slackConnectPayload,
1617
slackConnectUrl,
1718
slackDumpConnectModalView,
1819
slackDumpSavedModalView,
20+
slackFieldNoteDraftingModalView,
21+
slackFieldNoteDraftReadyModalView,
22+
slackFieldNoteDraftErrorModalView,
1923
slackSavedDumpPayload,
2024
openSlackDumpModal,
25+
openSlackFieldNoteDraftModal,
2126
openSlackRoomModal,
27+
updateSlackView,
2228
} from "../../../../src/server/slack";
2329
import { cleanString } from "../../../../src/server/validation";
2430

@@ -44,6 +50,13 @@ export async function POST(request) {
4450
triggerId: cleanString(payload.trigger_id, 200),
4551
});
4652
}
53+
if (actionId === "draft_team_read") {
54+
await openSlackFieldNoteDraftModal({
55+
teamId: slackTeamId(payload),
56+
slackUserId: cleanString(payload.user?.id, 80),
57+
triggerId: cleanString(payload.trigger_id, 200),
58+
});
59+
}
4760
} catch (error) {
4861
console.error("Slack App Home action failed", error);
4962
}
@@ -72,6 +85,37 @@ export async function POST(request) {
7285
}
7386
}
7487

88+
if (payload.type === "view_submission" && payload.view?.callback_id === "draft_team_read") {
89+
const teamId = slackTeamId(payload);
90+
const slackUserId = cleanString(payload.user?.id, 80);
91+
const selectedDumpIds = selectedSlackOptionValues(payload.view?.state?.values?.draft_dump_ids?.value?.selected_options);
92+
if (!selectedDumpIds.length) {
93+
return ok({
94+
response_action: "errors",
95+
errors: { draft_dump_ids: "choose at least one dump." },
96+
});
97+
}
98+
99+
after(async () => {
100+
try {
101+
const result = await createSlackFieldNoteDraft({ teamId, slackUserId, dumpIds: selectedDumpIds });
102+
await updateSlackView({
103+
teamId,
104+
viewId: cleanString(payload.view?.id, 120),
105+
view: slackFieldNoteDraftReadyModalView(result),
106+
});
107+
} catch (error) {
108+
await updateSlackView({
109+
teamId,
110+
viewId: cleanString(payload.view?.id, 120),
111+
view: slackFieldNoteDraftErrorModalView(error.message || "couldn't draft that field note yet."),
112+
});
113+
}
114+
});
115+
116+
return ok({ response_action: "update", view: slackFieldNoteDraftingModalView() });
117+
}
118+
75119
if (payload.type === "view_submission" && payload.view?.callback_id === "create_mumbl_room") {
76120
const teamId = slackTeamId(payload);
77121
const slackUserId = cleanString(payload.user?.id, 80);
@@ -130,6 +174,10 @@ function slackTeamId(payload) {
130174
return cleanString(payload.team?.id || payload.user?.team_id, 80);
131175
}
132176

177+
function selectedSlackOptionValues(options) {
178+
return Array.isArray(options) ? options.map((option) => cleanString(option?.value, 64)).filter(Boolean) : [];
179+
}
180+
133181
async function saveModalDump({ teamId, slackUserId, content }) {
134182
const sourceMeta = { trigger: "app_home_modal" };
135183
const existingConnection = await findSlackConnection({ teamId, slackUserId });

docs/free-tier-compromises.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Current compromise:
7676
- Slack is free during beta. There is no 30-day countdown, no credit card, and no usage gate.
7777
- The beta supports only explicit user actions: `/mumbl [text]` and the `save_to_mumbl` message shortcut.
7878
- `/mumbl room [team name]` can create a Mumbl room from Slack with an expiring creator handoff link. `/mumbl start [team name]` remains an alias.
79+
- App Home can create private dumps and draft a field note from selected recent private dumps. Drafting uses the existing field-note OpenAI daily limit, and publishing still requires review in Mumbl.
7980
- The Slack app requests `commands`, `users:read`, and `users:read.email` only. It does not request channel history or message history scopes.
8081
- Team-read posting to Slack is optional per room. When a creator enables it, Mumbl asks for an optional `chat:write` and `groups:write` permission upgrade only so it can create one private channel and post published team reads.
8182
- Slack daily check-in reminders are deferred because a 15-minute scheduler does not fit the free-tier posture.

src/components/DumpPageClient.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ export default function DumpPageClient({ mode = "home" }) {
6767

6868
useEffect(() => {
6969
setRecentSlug(getRecentSlug(""));
70+
const params = new URLSearchParams(window.location.search);
71+
if (params.get("fieldNote")) {
72+
setActivePanel("notes");
73+
setToast("draft ready. review it before it goes anywhere.");
74+
}
7075
}, []);
7176

7277
useEffect(() => {

0 commit comments

Comments
 (0)