Skip to content

Commit e5c0b09

Browse files
authored
Merge pull request #6892 from FlowFuse/6818-persist-expert-state
Persist expert state
2 parents 40c9b63 + e91b230 commit e5c0b09

File tree

5 files changed

+68
-6
lines changed

5 files changed

+68
-6
lines changed

frontend/src/components/ExpertButton.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ export default {
2424
computed: {
2525
...mapState(useUxDrawersStore, ['rightDrawer']),
2626
isExpertDrawerOpen () {
27-
return this.rightDrawer.state && this.rightDrawer.component?.name === 'ExpertDrawer'
27+
return (this.rightDrawer.state || this.rightDrawer.fixed)
2828
}
2929
},
3030
methods: {
3131
...mapActions('product/expert', ['openAssistantDrawer']),
3232
onClick () {
33-
this.openAssistantDrawer()
33+
const openOptions = {
34+
openPinned: this.rightDrawer.expertState.pinned
35+
}
36+
this.openAssistantDrawer(openOptions)
3437
}
3538
}
3639
}

frontend/src/components/drawers/RightDrawer.vue

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<section
33
id="right-drawer"
44
v-click-outside="{handler: closeDrawer, exclude: ['right-drawer']}"
5-
:class="{open: rightDrawer.state, wider: rightDrawer.wider, fixed: rightDrawer.fixed, resizing: isResizing, 'manually-resized': hasManuallyResized, pinning: isPinning, opening: isOpening, closing: isClosing}"
5+
:class="{open: rightDrawer.state, wider: rightDrawer.wider, fixed: rightDrawer.fixed, resizing: isResizing, 'manually-resized': hasManuallyResized, pinning: isPinning, opening: isOpening && !isPinning, closing: isClosing}"
66
:style="drawerStyle"
77
data-el="right-drawer"
88
>
@@ -43,6 +43,7 @@
4343

4444
<script>
4545
import { mapActions, mapState } from 'pinia'
46+
import { mapActions as mapVuexActions } from 'vuex'
4647
4748
import { useUxDrawersStore } from '@/stores/ux-drawers.js'
4849
@@ -105,10 +106,18 @@ export default {
105106
watch: {
106107
'rightDrawer.state': {
107108
handler (isOpen, wasOpen) {
109+
let reopenExpert = false
110+
const isExpertDrawer = this.rightDrawer.component?.name === 'ExpertDrawer'
111+
if (!isOpen && wasOpen && !isExpertDrawer) {
112+
// non expert drawer is closing - check if we need to re-open expert drawer
113+
reopenExpert = this.rightDrawer.expertState.pinned && this.rightDrawer.expertState.open
114+
}
115+
108116
// Set opening flag when drawer opens
109117
if (isOpen && !wasOpen) {
110118
this.isOpening = true
111119
this.isClosing = false
120+
this.isPinning = isExpertDrawer && this.rightDrawer.fixed // no animation if drawer is to open pinned
112121
// Clear opening flag after slide animation completes
113122
setTimeout(() => {
114123
this.isOpening = false
@@ -121,6 +130,10 @@ export default {
121130
// Clear closing flag after slide animation completes
122131
setTimeout(() => {
123132
this.isClosing = false
133+
// Re-open Expert drawer if needed after other drawer closes
134+
if (reopenExpert) {
135+
this.openAssistantDrawer({ openPinned: true })
136+
}
124137
}, 350)
125138
}
126139
@@ -166,6 +179,20 @@ export default {
166179
mounted () {
167180
// Add viewport resize listener
168181
window.addEventListener('resize', this.onViewportResize)
182+
183+
const openPinned = this.rightDrawer.expertState.open && this.rightDrawer.expertState.pinned
184+
185+
if (openPinned && this.shouldAllowPinning) {
186+
this.isPinning = true
187+
setTimeout(() => {
188+
this.openAssistantDrawer({ openPinned: true })
189+
setTimeout(() => {
190+
this.isPinning = false
191+
}, 200)
192+
}, 25)
193+
} else {
194+
this.isPinning = false
195+
}
169196
},
170197
beforeUnmount () {
171198
// Clean up resize listeners
@@ -177,6 +204,7 @@ export default {
177204
}
178205
},
179206
methods: {
207+
...mapVuexActions('product/expert', ['openAssistantDrawer']),
180208
...mapActions(useUxDrawersStore, ['closeRightDrawer', 'togglePinDrawer']),
181209
closeDrawer () {
182210
if (this.rightDrawer.state && this.rightDrawer.closeOnClickOutside) {

frontend/src/store/modules/product/expert/index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,12 +353,17 @@ const actions = {
353353
return expertApi.chat(payload)
354354
},
355355

356-
openAssistantDrawer ({ dispatch, rootGetters }) {
356+
openAssistantDrawer ({ dispatch, rootGetters }, options = {}) {
357357
if (rootGetters['account/featuresCheck'].isExpertAssistantFeatureEnabled === false) return
358358

359359
dispatch(`product/expert/${OPERATOR_AGENT}/getCapabilities`, null, { root: true })
360360

361-
return useUxDrawersStore().openRightDrawer({ component: markRaw(ExpertDrawer) })
361+
const openOptions = {
362+
component: markRaw(ExpertDrawer),
363+
fixed: options?.openPinned === true,
364+
closeOnClickOutside: options?.openPinned !== true
365+
}
366+
return useUxDrawersStore().openRightDrawer(openOptions)
362367
},
363368

364369
addWelcomeMessageIfNeeded ({ dispatch, state }) {

frontend/src/stores/ux-drawers.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export const useUxDrawersStore = defineStore('ux-drawers', {
2121
},
2222
rightDrawer: {
2323
state: false,
24+
expertState: {
25+
pinned: true,
26+
open: true
27+
},
2428
component: null,
2529
header: null,
2630
wider: false,
@@ -59,6 +63,11 @@ export const useUxDrawersStore = defineStore('ux-drawers', {
5963
if (this.rightDrawer.state && component.name === this.rightDrawer.component?.name) return
6064

6165
const openDrawer = () => {
66+
if (component.name === 'ExpertDrawer') {
67+
// save the ExpertDrawer pinned/open state (expertState is persistent)
68+
this.rightDrawer.expertState.pinned = fixed
69+
this.rightDrawer.expertState.open = true
70+
}
6271
this.rightDrawer.state = true
6372
this.rightDrawer.wider = wider
6473
this.rightDrawer.fixed = fixed
@@ -84,6 +93,11 @@ export const useUxDrawersStore = defineStore('ux-drawers', {
8493
},
8594

8695
closeRightDrawer () {
96+
if (this.rightDrawer.component?.name === 'ExpertDrawer') {
97+
// save the ExpertDrawer pinned/open state (expertState is persistent)
98+
this.rightDrawer.expertState.open = false
99+
this.rightDrawer.expertState.pinned = this.rightDrawer.fixed
100+
}
87101
// Set closing flag to prevent reopens during transition
88102
this.rightDrawer.closing = true
89103

@@ -167,8 +181,12 @@ export const useUxDrawersStore = defineStore('ux-drawers', {
167181
const newFixedState = !this.rightDrawer.fixed
168182
this.rightDrawer.fixed = newFixedState
169183
this.rightDrawer.pinned = newFixedState
170-
// When fixed, prevent close on click outside
171184
this.rightDrawer.closeOnClickOutside = !newFixedState
185+
if (this.rightDrawer.component?.name === 'ExpertDrawer') {
186+
// save the ExpertDrawer pinned/open state (expertState is persistent)
187+
this.rightDrawer.expertState.open = this.rightDrawer.state
188+
this.rightDrawer.expertState.pinned = newFixedState
189+
}
172190

173191
// Always close overlay when toggling (whether fixing or unfixing)
174192
const uxStore = useUxStore()
@@ -192,5 +210,9 @@ export const useUxDrawersStore = defineStore('ux-drawers', {
192210
setLeftDrawer (component) {
193211
this.leftDrawer.component = component ? markRaw(component) : null
194212
}
213+
},
214+
persist: {
215+
pick: ['rightDrawer.expertState'],
216+
storage: localStorage
195217
}
196218
})

test/unit/frontend/stores/ux-drawers.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ describe('ux-drawers store', () => {
7474
expect(store.rightDrawer.props).toEqual({})
7575
expect(store.rightDrawer.on).toEqual({})
7676
expect(store.rightDrawer.bind).toEqual({})
77+
expect(store.rightDrawer.expertState).toEqual({
78+
pinned: true,
79+
open: true
80+
})
7781
})
7882

7983
it('passes through all options', () => {

0 commit comments

Comments
 (0)