From cc3f42800883ab05386cf122e2bca4537caf0768 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Fri, 7 Aug 2026 19:53:46 +0530 Subject: [PATCH] count macro usage for content-only macros Macros with no actions never hit the apply endpoint, so their usage count stayed at zero. The frontend now calls apply whenever a macro was picked, and drafts store the macro id so a macro survives switching conversations. --- cmd/macro.go | 2 +- .../apps/main/src/composables/useDraftManager.js | 10 ++++++++-- .../features/conversation/CreateConversation.vue | 8 +++----- .../main/src/features/conversation/ReplyBox.vue | 14 ++++++++------ 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/cmd/macro.go b/cmd/macro.go index e0257886..38895c72 100644 --- a/cmd/macro.go +++ b/cmd/macro.go @@ -199,7 +199,7 @@ func handleApplyMacro(r *fastglue.Request) error { } } - if successCount == 0 { + if len(incomingActions) > 0 && successCount == 0 { return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.T("macro.couldNotApply"), nil, envelope.GeneralError) } diff --git a/frontend/apps/main/src/composables/useDraftManager.js b/frontend/apps/main/src/composables/useDraftManager.js index ce539622..310baab1 100644 --- a/frontend/apps/main/src/composables/useDraftManager.js +++ b/frontend/apps/main/src/composables/useDraftManager.js @@ -36,6 +36,7 @@ const draftKey = (uuid, type) => `${uuid}::${type}` const metaSignature = (meta) => JSON.stringify({ + macro_id: meta?.macro_id || 0, macro_actions: meta?.macro_actions || [], attachments: (meta?.attachments || []).map(a => a.uuid) }) @@ -52,6 +53,7 @@ export function useDraftManager (conversationUUID, messageType, uploadedFiles = const isLoading = ref(false) const loadedAttachments = ref([]) const loadedMacroActions = ref([]) + const loadedMacroID = ref(0) // Live-key guard: the editor is transiently empty during open/switch and must not clobber a stored draft. const loadedKey = ref(null) @@ -59,7 +61,9 @@ export function useDraftManager (conversationUUID, messageType, uploadedFiles = const buildDraft = () => { const meta = {} - const macroActions = conversationStore.getMacro(MACRO_CONTEXT.REPLY)?.actions || [] + const macro = conversationStore.getMacro(MACRO_CONTEXT.REPLY) + const macroActions = macro?.actions || [] + if (macro?.id > 0) meta.macro_id = macro.id if (macroActions.length > 0) meta.macro_actions = macroActions if (uploadedFiles?.value?.length > 0) { meta.attachments = uploadedFiles.value.map(file => ({ @@ -80,6 +84,7 @@ export function useDraftManager (conversationUUID, messageType, uploadedFiles = textContent.value = '' loadedAttachments.value = validateAttachments(draft?.meta?.attachments) loadedMacroActions.value = validateMacroActions(draft?.meta?.macro_actions) + loadedMacroID.value = Number(draft?.meta?.macro_id) > 0 ? Number(draft.meta.macro_id) : 0 } const load = async (uuid, type) => { @@ -168,6 +173,7 @@ export function useDraftManager (conversationUUID, messageType, uploadedFiles = isLoading, clearDraft, loadedAttachments, - loadedMacroActions + loadedMacroActions, + loadedMacroID } } diff --git a/frontend/apps/main/src/features/conversation/CreateConversation.vue b/frontend/apps/main/src/features/conversation/CreateConversation.vue index 8592f897..b2d8872a 100644 --- a/frontend/apps/main/src/features/conversation/CreateConversation.vue +++ b/frontend/apps/main/src/features/conversation/CreateConversation.vue @@ -478,7 +478,7 @@ const createConversation = form.handleSubmit(async (values) => { // Get macro from context, and set if any actions are available. const macro = conversationStore.getMacro(MACRO_CONTEXT.NEW_CONVERSATION) - if (conversationUUID !== '' && macro?.id && macro?.actions?.length > 0) { + if (conversationUUID !== '' && macro?.id) { try { await api.applyMacro(conversationUUID, macro.id, macro.actions) } catch (error) { @@ -506,10 +506,8 @@ const createConversation = form.handleSubmit(async (values) => { watch( () => conversationStore.getMacro(MACRO_CONTEXT.NEW_CONVERSATION).id, () => { - form.setFieldValue( - 'content', - conversationStore.getMacro(MACRO_CONTEXT.NEW_CONVERSATION).message_content - ) + const content = conversationStore.getMacro(MACRO_CONTEXT.NEW_CONVERSATION).message_content + if (content) form.setFieldValue('content', content) }, { deep: true } ) diff --git a/frontend/apps/main/src/features/conversation/ReplyBox.vue b/frontend/apps/main/src/features/conversation/ReplyBox.vue index 0ac38b67..2f9ccc2c 100644 --- a/frontend/apps/main/src/features/conversation/ReplyBox.vue +++ b/frontend/apps/main/src/features/conversation/ReplyBox.vue @@ -190,7 +190,8 @@ const { isLoading: isDraftLoading, clearDraft, loadedAttachments, - loadedMacroActions + loadedMacroActions, + loadedMacroID } = useDraftManager(currentConversationUUID, messageType, mediaFiles) // Rest of existing state @@ -401,7 +402,7 @@ const processSend = async (skipContactEmailCheck = false, skipMissingTagsCheck = if (!hasMessageSendingErrored) { const macroID = conversationStore.getMacro(MACRO_CONTEXT.REPLY)?.id const macroActions = conversationStore.getMacro(MACRO_CONTEXT.REPLY)?.actions || [] - if (macroID > 0 && macroActions.length > 0) { + if (macroID > 0) { try { await api.applyMacro(convUUID, macroID, macroActions) } catch (error) { @@ -444,12 +445,13 @@ watch( { deep: true } ) -// Reset first so a loaded draft never inherits the previous conversation's macro id/message_content (drafts store only actions). +// Reset first so a loaded draft never inherits the previous conversation's macro (drafts store no message_content). watch( - loadedMacroActions, - (actions) => { + [loadedMacroID, loadedMacroActions], + ([id, actions]) => { conversationStore.resetMacro(MACRO_CONTEXT.REPLY) - if (actions.length) conversationStore.setMacroActions([...toRaw(actions)], MACRO_CONTEXT.REPLY) + if (id > 0) conversationStore.setMacro({ id, actions: [...toRaw(actions)] }, MACRO_CONTEXT.REPLY) + else if (actions.length) conversationStore.setMacroActions([...toRaw(actions)], MACRO_CONTEXT.REPLY) }, { deep: true } )