@@ -9,9 +9,8 @@ import { SlackCommandContext, SlackActionContext } from '../../interfaces/slack-
99
1010export type SlackDAORequest = Pick < SlackActionContext , 'body' | 'session' | 'ack' | 'respond' > ;
1111import { slackMessages , replacePlaceholders } from '@notification-system/messages' ;
12- import type { ViewStateSelectedOption } from '@slack/bolt' ;
1312import {
14- daoSelectionList ,
13+ daoToggleList ,
1514 errorMessage ,
1615 daoEmptyState ,
1716 daoListWithEdit
@@ -50,10 +49,10 @@ export class SlackDAOService extends BaseDAOService {
5049 const userPreferences = await this . getUserSubscriptions ( fullUserId ) ;
5150 const currentSelections = new Set ( userPreferences ) ;
5251
53- const blocks = daoSelectionList (
52+ const blocks = daoToggleList (
5453 daos ,
5554 currentSelections ,
56- 'dao_checkboxes ' ,
55+ 'dao_toggle ' ,
5756 'dao_confirm_subscribe' ,
5857 slackMessages . dao . subscribeInstructions
5958 ) ;
@@ -121,64 +120,83 @@ export class SlackDAOService extends BaseDAOService {
121120 }
122121
123122 /**
124- * Confirm DAO selection changes from checkboxes
123+ * Toggle a single DAO subscription on/off, then re-render the list in place.
124+ * Option 2 UI: each DAO is a button that saves immediately on click — there is
125+ * no batch "confirm" step for the selection itself.
125126 */
126- async confirm ( context : SlackActionContext ) : Promise < void > {
127- const channelId = context . body . channel ?. id ;
128- const workspaceId = context . body . team ?. id || context . body . user ?. team_id ;
127+ async toggle ( context : SlackActionContext , daoId : string ) : Promise < void > {
128+ const channelId = context . body . channel ?. id || context . body . channel_id ;
129+ const workspaceId = context . body . team ?. id || context . body . team_id || context . body . user ?. team_id ;
129130 const fullUserId = `${ workspaceId } :${ channelId } ` ;
130131
131132 try {
132133 await context . ack ( ) ;
133134
134- // Extract selected DAOs from checkbox state
135- const state = context . body . state ;
136- if ( typeof state === 'string' ) {
137- throw new Error ( 'Unexpected DialogAction state format' ) ;
135+ const normalized = daoId . toUpperCase ( ) ;
136+ const daos = await this . fetchAvailableDAOs ( ) ;
137+ const current = new Set (
138+ await this . subscriptionApi . getUserPreferences ( fullUserId , this . getPlatformId ( ) , daos . map ( dao => dao . id ) )
139+ ) ;
140+
141+ // Flip just the clicked DAO and persist that single change
142+ const willSubscribe = ! current . has ( normalized ) ;
143+ await this . subscriptionApi . saveUserPreference ( normalized , fullUserId , this . getPlatformId ( ) , willSubscribe ) ;
144+ if ( willSubscribe ) {
145+ current . add ( normalized ) ;
146+ } else {
147+ current . delete ( normalized ) ;
138148 }
139149
140- const selectedOptions : ViewStateSelectedOption [ ] =
141- state ?. values ?. dao_checkboxes_block ?. dao_checkboxes ?. selected_options || [ ] ;
142- const selectedDAOs = new Set < string > ( selectedOptions . map ( opt => opt . value ) ) ;
150+ if ( context . respond ) {
151+ await context . respond ( {
152+ blocks : daoToggleList ( daos , current , 'dao_toggle' , 'dao_confirm_subscribe' , slackMessages . dao . subscribeInstructions ) ,
153+ response_type : 'in_channel' ,
154+ replace_original : true
155+ } ) ;
156+ }
157+ } catch ( error ) {
158+ this . logger . error ( { err : error , event : 'dao.toggle_failed' } , 'error toggling DAO subscription' ) ;
159+ if ( context . respond ) {
160+ await context . respond ( {
161+ text : slackMessages . dao . updateError ,
162+ response_type : 'in_channel'
163+ } ) ;
164+ }
165+ }
166+ }
143167
144- // Sync to the complete desired state (handles both adds and removes)
145- await this . syncSubscriptionsToState ( fullUserId , selectedDAOs ) ;
168+ /**
169+ * "Done" button. Subscriptions were already saved per-click via toggle(), so
170+ * this just collapses the button list into a final summary. Onboarding advance
171+ * (the wallet step) is triggered by the action handler in slack-bot.service.
172+ */
173+ async confirm ( context : SlackActionContext ) : Promise < void > {
174+ const channelId = context . body . channel ?. id || context . body . channel_id ;
175+ const workspaceId = context . body . team ?. id || context . body . team_id || context . body . user ?. team_id ;
176+ const fullUserId = `${ workspaceId } :${ channelId } ` ;
146177
147- // Show confirmation message
148- let successMessage : string ;
149- if ( selectedDAOs . size === 0 ) {
150- successMessage = slackMessages . dao . unsubscribeAllSuccess ;
151- } else {
152- const daoList = this . formatDAOList ( selectedDAOs ) ;
153- successMessage = replacePlaceholders ( slackMessages . dao . subscribeSuccess , { daoList } ) ;
154- }
178+ try {
179+ await context . ack ( ) ;
180+
181+ const daos = await this . fetchAvailableDAOs ( ) ;
182+ const current = await this . subscriptionApi . getUserPreferences ( fullUserId , this . getPlatformId ( ) , daos . map ( dao => dao . id ) ) ;
183+
184+ const summaryMessage = current . length === 0
185+ ? slackMessages . dao . unsubscribeAllSuccess
186+ : replacePlaceholders ( slackMessages . dao . subscribeSuccess , { daoList : this . formatDAOList ( current ) } ) ;
155187
156188 if ( context . respond ) {
157189 await context . respond ( {
158- replace_original : false ,
190+ replace_original : true ,
159191 blocks : [
160- {
161- type : 'section' ,
162- text : {
163- type : 'mrkdwn' ,
164- text : successMessage
165- }
166- } ,
167- {
168- type : 'context' ,
169- elements : [
170- {
171- type : 'mrkdwn' ,
172- text : slackMessages . dao . updateInstructions
173- }
174- ]
175- }
192+ { type : 'section' , text : { type : 'mrkdwn' , text : summaryMessage } } ,
193+ { type : 'context' , elements : [ { type : 'mrkdwn' , text : slackMessages . dao . updateInstructions } ] }
176194 ] ,
177195 response_type : 'in_channel'
178196 } ) ;
179197 }
180198 } catch ( error ) {
181- this . logger . error ( { err : error , event : 'dao.update_failed ' } , 'error updating subscriptions ' ) ;
199+ this . logger . error ( { err : error , event : 'dao.confirm_failed ' } , 'error finalizing DAO selection ' ) ;
182200 if ( context . respond ) {
183201 await context . respond ( {
184202 replace_original : false ,
0 commit comments