Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not stack several automatic dialogs #1549

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

ArturoManzoli
Copy link
Contributor

@ArturoManzoli ArturoManzoli commented Jan 7, 2025

  • Implemented a dialog queue to handle potential modal stack-ups.

Closes #1494

Comment on lines 2 to 4
<v-dialog v-model="internalShowDialog" :persistent="persistent" :width="maxWidth || 'auto'">
<v-dialog
v-model="internalShowDialog"
:persistent="persistent"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case are we coupling the interaction dialog to the queue system? It seems strange, since we use it for a lot of popups that are open explicitly by the user (and not automatically), like the about page, menus, error dialogs, etc.

It seems to me they should be completely uncoupled, and the queue system should live outside it and only for automatically-issued dialogs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other dialogs will be unchanged and uncoupled from the queue. The ones that will be queued are only those that can potentially stack up, like the ones on the Cockpit startup.

It's also done like this on snackbar queue systems. Whenever a message can stackup, you queue it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean that the queue system should be external to the InteractionDialog.vue component, otherwise we are putting queue logic on dialogs that should not be queue-aware.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In reality the InteractionDialog is uncoupled from the queue system.

The queue uses a logic based on dependency injection. If you check on App.vue, the components are loaded on startup and queued independently and the enqueueDialog function takes the component, its props and an id as parameter.

interfaceStore.enqueueDialog({ id: 'VehicleDiscoveryDialog', component: VehicleDiscoveryDialog, props: {}, })

For example, the system update dialog will only be queued if the function that checks for new versions triggers the dialog and will inject the component on the function to do so.
Actually it doesn't have to be a dialog. Could be virtually any other component we have.

Copy link
Member

@rafaellehmkuhl rafaellehmkuhl Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm saying is that the interaction dialog should not be the one deciding on what happens with the log queue (interfaceStore.openNextDialogOnQueue()), but instead there should be an observer outside the interaction dialog checking if there's any interaction dialog open, and if not, open the next dialog in the queue.

The interaction dialog should be what is is, a dialog. It does not need to be aware of the queue. It's an unnecessary architecture coupling.

It's the same problem we had before, that you're fixing in this PR, about the interaction dialog being aware of the Tutorial functionality (it shouldn't).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the InteractionDialog isn't aware of the queue. The only modification I made to it was the closing logic so it can handle the queue

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed the details in a call. Main topic was to uncouple to facilitate maintenance, shrink the codebase and allow easy future changes (for new auto-instantiated dialogs).

Comment on lines 2 to 4
<v-dialog v-model="internalShowDialog" :persistent="persistent" :width="maxWidth || 'auto'">
<v-dialog
v-model="internalShowDialog"
:persistent="persistent"
Copy link
Member

@rafaellehmkuhl rafaellehmkuhl Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I'm saying is that the interaction dialog should not be the one deciding on what happens with the log queue (interfaceStore.openNextDialogOnQueue()), but instead there should be an observer outside the interaction dialog checking if there's any interaction dialog open, and if not, open the next dialog in the queue.

The interaction dialog should be what is is, a dialog. It does not need to be aware of the queue. It's an unnecessary architecture coupling.

It's the same problem we had before, that you're fixing in this PR, about the interaction dialog being aware of the Tutorial functionality (it shouldn't).

@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch 2 times, most recently from d6147e2 to f6e9ea4 Compare January 17, 2025 15:49
@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch from f6e9ea4 to 38901db Compare January 27, 2025 15:15
@ArturoManzoli
Copy link
Contributor Author

On last patch:

  • Dialog queue now monitors the dom for its open elements by id, and once they cease to exist, they open the next one on the line, decoupling the enqueued elements from the queue logic.
  • Reverted changes made on components like UpdateNotification, VehicleDiscoveryDialog and InteractiveDialog;

@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch 2 times, most recently from 1495c59 to c5cb8ce Compare January 27, 2025 15:59
src/App.vue Outdated
Comment on lines 316 to 317
<Tutorial :show-tutorial="interfaceStore.isTutorialVisible" />
<VideoLibraryModal :open-modal="interfaceStore.isVideoLibraryVisible" />
<VehicleDiscoveryDialog v-model="showDiscoveryDialog" show-auto-search-option />
<UpdateNotification v-if="isElectron()" />
<Teleport to="body">
<Tutorial />
</Teleport>

<!-- Startup dialogs queue -->
<div v-if="activeDialog">
<component :is="activeDialog.component" v-bind="activeDialog.props" :id="activeDialog.id" />
</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the tutorial treated differently? Aren't it a automatic dialog like all others?

@@ -403,6 +392,7 @@ const handleKeydown = (event: KeyboardEvent): void => {
watch(
() => interfaceStore.isTutorialVisible,
(val) => {
console.log('🚀 ~ val:', val)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was in an old PR, was asked to be removed, and is being added here again.

Comment on lines 82 to 85
if (interfaceStore.activeDialog?.id === 'UpdateNotification' || interfaceStore.activeDialog === undefined) {
showUpdateDialog.value = true
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still putting the instantiation logic inside the components, just with another name/approach, and there's no need for that.

This is solvable with a much easier approach where the one controlling the issue of automatic notifications (the App.vue file) is the one who instantiates the component, based on the queue logic (show tutorial, when it closes show the discovery dialog, when it closes show the user selection, etc). There's no need for any logic around the automation of the component mounting to be inside the component itself.

Every new dialog component still has to implement logic around an automatic issuing, which it should't need to be aware off. The dialog components should only care about their own content.

@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch from c5cb8ce to 2b70ec8 Compare January 27, 2025 17:19
@ArturoManzoli ArturoManzoli marked this pull request as draft January 27, 2025 17:19
@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch from 2b70ec8 to d0ea490 Compare January 27, 2025 18:32
@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch 4 times, most recently from c725537 to f4753c6 Compare February 17, 2025 20:12
@ArturoManzoli
Copy link
Contributor Author

ArturoManzoli commented Feb 17, 2025

@rafaellehmkuhl,
I have updated the approach to the dialog queue. Now there is a composable that controls the queue and it only needs the dialogs to have a v-model and emit its update to it on close.
Now it works fine on both Electron and Web versions.

I think it's a cleaner and more efficient way to solve the dialog stack issue Cockpit have.

I had to slightly modify the opening logic on UpdateNotification.vue, besides the regular props.modelValue and emit.
Since I didn't have the option of conditionally adding this dialog to queue, I added a closing on no update available. This way the component unclogged the queue and life went on normally again :)

@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch from f4753c6 to e352580 Compare February 17, 2025 22:58
@ArturoManzoli ArturoManzoli marked this pull request as ready for review February 17, 2025 23:34
@ArturoManzoli ArturoManzoli force-pushed the do-not-stack-auto-dialogs branch from e352580 to 0271bb7 Compare February 18, 2025 10:27
Comment on lines -313 to +317
<Tutorial v-if="interfaceStore.isTutorialVisible" />
<VideoLibraryModal v-if="interfaceStore.isVideoLibraryVisible" />
<VehicleDiscoveryDialog v-model="showDiscoveryDialog" show-auto-search-option />
<UpdateNotification v-if="isElectron()" />
<Tutorial :model-value="interfaceStore.isTutorialVisible" />
<div v-if="WrappedDialog">
<component :is="WrappedDialog" />
</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To better understand: shouldn't the Tutorial be used in the queue system, since it's opened automatically as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not for the queue. this for the manual opening on the General settings. There is also a similar implementation for the vehicle discovery dialog.
The other way to manually open those, is to create a single item queue for them when manually called.

Copy link
Member

@rafaellehmkuhl rafaellehmkuhl Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if we open those manually and they are triggered to be open automatically, there will be two instances of them in the screen, not?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe yes, but I see them far from each other in the sense of UI and how the user will use either the auto pop up and the manual instance of it.

Would be a big problem if the user manage to intentionally open two instances of those dialogs?

If so, is better to also enqueue the manual opening.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a problem with this as long as they are not the same dialog.

If the user has a tutorial that was automatically open, it's fine if they open a vehicle discovery one manually, and this manual one gets on top of the other, but if the user has a vehicle discovery manually opened, an automatic one should not be opened over it.

Does it make sense?

const showTutorial = ref(true)
const props = defineProps<{
/**
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing docs.

import { app_version } from '@/libs/cosmos'
import { isElectron } from '@/libs/utils'

const props = defineProps<{
/**
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing docs

if (props.modelValue && updateAvailable.value) {
showUpdateDialog.value = true
} else {
openSnackbar({ message: 'No updates available.', variant: 'success' })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This notification is wrong. If there's an update available but the modelValue props is false, it will enter here.

Copy link
Member

@rafaellehmkuhl rafaellehmkuhl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's apparently a bug in the logic.

A tutorial is always opening here, even if I click not to show it again.

Kapture.2025-02-19.at.09.24.39.mp4

@ArturoManzoli
Copy link
Contributor Author

There's apparently a bug in the logic.

A tutorial is always opening here, even if I click not to show it again.

Yep, the don't show again button behaves correctly on step 1. On other steps it's indeed not saving on the storage.
Fixing that...

Copy link
Member

@rafaellehmkuhl rafaellehmkuhl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR is reviving a bug I solved some PRs ago, where the Tutorial is affecting the UI even when not opened.

This is happening because the v-if was removed from the Tutorial component.

image

@@ -208,31 +219,27 @@ const handleStepChange = (newStep: number): void => {
interfaceStore.componentToHighlight = 'menu-trigger'
interfaceStore.currentSubMenuComponentName = null
interfaceStore.currentSubMenuName = null
interfaceStore.userHasSeenTutorial = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that being removed?

It makes sure the Tutorial is reopened if the user clicked to change a step, which seems like the logical behavior.

@@ -332,7 +333,6 @@ const alwaysShowTutorialOnStartup = (): void => {

const nextTutorialStep = (): void => {
if (currentTutorialStep.value === steps.length) {
dontShowTutorialAgain()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. It makes sure the Tutorial is not reopened if the user has seem it entirely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

There's a stack of dialogs being created on top of each other without coordination
2 participants