Skip to content

Commit

Permalink
fix: add 'Generate summary' button
Browse files Browse the repository at this point in the history
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
  • Loading branch information
Antreesy authored and backportbot[bot] committed Nov 22, 2024
1 parent 9abf229 commit 30a6470
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 11 deletions.
47 changes: 36 additions & 11 deletions src/components/MessagesList/MessagesGroup/Message/Message.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
:title="t('spreed', 'Show or collapse system messages')"
@click="toggleCombinedSystemMessage">
<template #icon>
<UnfoldMore v-if="isCombinedSystemMessageCollapsed" />
<UnfoldLess v-else />
<IconUnfoldMore v-if="isCombinedSystemMessageCollapsed" />
<IconUnfoldLess v-else />
</template>
</NcButton>
</div>
Expand All @@ -82,6 +82,13 @@
class="message-unread-marker">
<div class="message-unread-marker__wrapper">
<span class="message-unread-marker__text">{{ t('spreed', 'Unread messages') }}</span>
<NcButton v-if="shouldShowSummaryOption"
@click="generateSummary">
<template #icon>
<IconCreation />
</template>
{{ t('spreed', 'Generate summary') }}
</NcButton>
</div>
</div>
</li>
Expand All @@ -90,8 +97,9 @@
<script>
import { vIntersectionObserver as IntersectionObserver } from '@vueuse/components'

import UnfoldLess from 'vue-material-design-icons/UnfoldLessHorizontal.vue'
import UnfoldMore from 'vue-material-design-icons/UnfoldMoreHorizontal.vue'
import IconCreation from 'vue-material-design-icons/Creation.vue'
import IconUnfoldLess from 'vue-material-design-icons/UnfoldLessHorizontal.vue'
import IconUnfoldMore from 'vue-material-design-icons/UnfoldMoreHorizontal.vue'

import { showError, showSuccess, showWarning, TOAST_DEFAULT_TIMEOUT } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'
Expand All @@ -112,24 +120,27 @@ import Poll from './MessagePart/Poll.vue'
import Reactions from './MessagePart/Reactions.vue'

import { CONVERSATION, MENTION, PARTICIPANT } from '../../../../constants.js'
import { getTalkConfig } from '../../../../services/CapabilitiesManager.ts'
import { getTalkConfig, hasTalkFeature } from '../../../../services/CapabilitiesManager.ts'
import { EventBus } from '../../../../services/EventBus.ts'
import { useChatExtrasStore } from '../../../../stores/chatExtras.js'
import { getItemTypeFromMessage } from '../../../../utils/getItemTypeFromMessage.ts'

const canSummarizeChat = hasTalkFeature('local', 'chat-summary-api')
const summaryThreshold = getTalkConfig('local', 'chat', 'summary-threshold') ?? 0

export default {
name: 'Message',

components: {
IconCreation,
IconUnfoldLess,
IconUnfoldMore,
MessageBody,
MessageButtonsBar,
MessageForwarder,
MessageTranslateDialog,
NcButton,
Reactions,
// Icons
UnfoldLess,
UnfoldMore,
},

directives: {
Expand Down Expand Up @@ -224,9 +235,19 @@ export default {
if (this.isLastMessage) {
return false
}
return (!this.isCollapsedSystemMessage && this.message.id === this.visualLastLastReadMessageId)
|| (this.isCollapsedSystemMessage && this.message.id === this.visualLastLastReadMessageId && this.message.id !== this.lastCollapsedMessageId)
|| (this.isCombinedSystemMessage && this.lastCollapsedMessageId === this.visualLastLastReadMessageId)

if (this.message.id === this.visualLastLastReadMessageId) {
return !this.isCollapsedSystemMessage || this.message.id !== this.lastCollapsedMessageId
}

return this.isCombinedSystemMessage && this.lastCollapsedMessageId === this.visualLastLastReadMessageId
},

shouldShowSummaryOption() {
if (!canSummarizeChat || this.chatExtrasStore.hasChatSummaryTaskRequested(this.message.token)) {
return false
}
return (this.conversation.unreadMessages >= summaryThreshold)
},

isSystemMessage() {
Expand Down Expand Up @@ -426,6 +447,10 @@ export default {
toggleFollowUpEmojiPicker() {
this.isFollowUpEmojiPickerOpen = !this.isFollowUpEmojiPickerOpen
},

async generateSummary() {
await this.chatExtrasStore.requestChatSummary(this.message.token, this.message.id)
}
},
}
</script>
Expand Down
16 changes: 16 additions & 0 deletions src/services/messagesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import type {
receiveMessagesResponse,
setReadMarkerParams,
setReadMarkerResponse,
summarizeChatParams,
summarizeChatResponse,
} from '../types/index.ts'

type ReceiveMessagesPayload = Partial<receiveMessagesParams> & { token: string }
Expand Down Expand Up @@ -209,6 +211,19 @@ const setConversationUnread = async function(token: string, options?: object): m
return axios.delete(generateOcsUrl('apps/spreed/api/v1/chat/{token}/read', { token }, options), options)
}

/**
* Request chat summary from a given message
*
* @param token The conversation token
* @param fromMessageId The last read message to start from
* @param options object destructured
*/
const summarizeChat = async function(token: string, fromMessageId: summarizeChatParams['fromMessageId'], options?: object): summarizeChatResponse {
return axios.post(generateOcsUrl('apps/spreed/api/v1/chat/{token}/summarize', { token }, options), {
fromMessageId,
} as summarizeChatParams, options)
}

export {
fetchMessages,
lookForNewMessages,
Expand All @@ -220,4 +235,5 @@ export {
postRichObjectToConversation,
updateLastReadMessage,
setConversationUnread,
summarizeChat,
}
34 changes: 34 additions & 0 deletions src/stores/chatExtras.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { generateUrl, getBaseUrl } from '@nextcloud/router'
import BrowserStorage from '../services/BrowserStorage.js'
import { getUpcomingEvents } from '../services/conversationsService.js'
import { EventBus } from '../services/EventBus.ts'
import { summarizeChat } from '../services/messagesService.ts'
import { getUserAbsence } from '../services/participantsService.js'
import { parseSpecialSymbols, parseMentions } from '../utils/textParse.ts'

Expand Down Expand Up @@ -41,6 +42,7 @@ export const useChatExtrasStore = defineStore('chatExtras', {
chatEditInput: {},
tasksCount: 0,
tasksDoneCount: 0,
chatSummary: {},
}),

getters: {
Expand All @@ -61,6 +63,14 @@ export const useChatExtrasStore = defineStore('chatExtras', {
getNextEvent: (state) => (token) => {
return state.upcomingEvents[token]?.[0]
},

getChatSummaryTaskQueue: (state) => (token) => {
return Object.values(Object(state.chatSummary[token]))
},

hasChatSummaryTaskRequested: (state) => (token) => {
return state.chatSummary[token] !== undefined
},
},

actions: {
Expand Down Expand Up @@ -246,6 +256,30 @@ export const useChatExtrasStore = defineStore('chatExtras', {
setTasksCounters({ tasksCount, tasksDoneCount }) {
this.tasksCount = tasksCount
this.tasksDoneCount = tasksDoneCount
},

async requestChatSummary(token, fromMessageId) {
try {
const response = await summarizeChat(token, fromMessageId)
if (!response.data) {
console.warn('No messages found to summarize:', { token, fromMessageId })
return
}
const task = response.data.ocs.data

if (!this.chatSummary[token]) {
Vue.set(this.chatSummary, token, {})
}
Vue.set(this.chatSummary[token], fromMessageId, {
...task,
fromMessageId,
})
if (task.nextOffset && task.nextOffset !== fromMessageId) {
await this.requestChatSummary(token, task.nextOffset)
}
} catch (error) {
console.error('Error while requesting a summary:', error)
}
}
},
})
2 changes: 2 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ export type postRichObjectResponse = ApiResponse<operations['chat-share-object-t
export type setReadMarkerParams = Required<operations['chat-set-read-marker']>['requestBody']['content']['application/json']
export type setReadMarkerResponse = ApiResponse<operations['chat-set-read-marker']['responses'][200]['content']['application/json']>
export type markUnreadResponse = ApiResponse<operations['chat-mark-unread']['responses'][200]['content']['application/json']>
export type summarizeChatParams = operations['chat-summarize-chat']['requestBody']['content']['application/json']
export type summarizeChatResponse = ApiResponse<operations['chat-summarize-chat']['responses'][201]['content']['application/json']>

// Avatars
export type setFileAvatarResponse = ApiResponse<operations['avatar-upload-avatar']['responses'][200]['content']['application/json']>
Expand Down

0 comments on commit 30a6470

Please sign in to comment.