-
Notifications
You must be signed in to change notification settings - Fork 131
frontend: show snackbar notifications on parameter changes #3868
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,56 +1,127 @@ | ||
| <template> | ||
| <v-snackbar | ||
| v-model="show" | ||
| :timeout="timeout" | ||
| > | ||
| {{ message }} | ||
|
|
||
| <template #action="{ attrs }"> | ||
| <v-btn | ||
| :color="color" | ||
| text | ||
| v-bind="attrs" | ||
| @click="show = false" | ||
| <div class="alerter-stack"> | ||
| <v-slide-y-reverse-transition | ||
| group | ||
| leave-absolute | ||
| > | ||
| <v-alert | ||
| v-for="alert in alerts" | ||
| :key="alert.id" | ||
| :value="true" | ||
| :type="alertType(alert.level)" | ||
| class="alerter-item" | ||
| dense | ||
| dismissible | ||
| elevation="6" | ||
| @input="dismiss(alert.id)" | ||
| > | ||
| Close | ||
| </v-btn> | ||
| </template> | ||
| </v-snackbar> | ||
| {{ alert.message }} | ||
| </v-alert> | ||
| </v-slide-y-reverse-transition> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script lang="ts"> | ||
| import Vue from 'vue' | ||
|
|
||
| import message_manager, { MessageLevel } from '@/libs/message-manager' | ||
|
|
||
| const MAX_VISIBLE = 5 | ||
| const DRAIN_INTERVAL_MS = 1000 | ||
|
|
||
| interface AlertEntry { | ||
| id: number | ||
| level: MessageLevel | ||
| message: string | ||
| } | ||
|
|
||
| let nextId = 0 | ||
|
|
||
| export default Vue.extend({ | ||
| name: 'ErrorMessage', | ||
| data() { | ||
| return { | ||
| level: undefined as MessageLevel|undefined, | ||
| message: '', | ||
| show: false, | ||
| alerts: [] as AlertEntry[], | ||
| queue: [] as AlertEntry[], | ||
|
sourcery-ai[bot] marked this conversation as resolved.
|
||
| drainTimer: null as ReturnType<typeof setInterval> | null, | ||
| boundCallback: null as ((level: MessageLevel, msg: string) => void) | null, | ||
| } | ||
| }, | ||
| mounted() { | ||
| this.boundCallback = (level: MessageLevel, message: string) => { | ||
| nextId += 1 | ||
| const entry = { id: nextId, level, message } | ||
| if (this.alerts.length < MAX_VISIBLE) { | ||
| this.showAlert(entry) | ||
| } else { | ||
| this.queue.push(entry) | ||
| this.startDrain() | ||
| } | ||
| } | ||
| message_manager.addCallback(this.boundCallback) | ||
| }, | ||
| beforeDestroy() { | ||
| if (this.boundCallback) { | ||
| message_manager.removeCallback(this.boundCallback) | ||
| this.boundCallback = null | ||
| } | ||
| this.stopDrain() | ||
| }, | ||
| computed: { | ||
| color(): string { | ||
| switch (this.level) { | ||
| methods: { | ||
| showAlert(entry: AlertEntry) { | ||
| this.alerts.push(entry) | ||
| const timeout = this.getTimeout(entry.level) | ||
| if (timeout > 0) { | ||
| setTimeout(() => this.dismiss(entry.id), timeout) | ||
| } | ||
| }, | ||
| dismiss(id: number) { | ||
| const idx = this.alerts.findIndex((a) => a.id === id) | ||
| if (idx !== -1) { | ||
| this.alerts.splice(idx, 1) | ||
| this.promoteFromQueue() | ||
| } | ||
| }, | ||
| promoteFromQueue() { | ||
| while (this.queue.length > 0 && this.alerts.length < MAX_VISIBLE) { | ||
| this.showAlert(this.queue.shift()!) | ||
| } | ||
| if (this.queue.length === 0) { | ||
| this.stopDrain() | ||
| } | ||
| }, | ||
| startDrain() { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not following the logic here, dismiss removes the alert but it's not taking in consideration the timeout at all, so all notifications will only show for a second. Is that correct ? |
||
| if (this.drainTimer) return | ||
| this.drainTimer = setInterval(() => { | ||
| const evictIdx = this.alerts.findIndex((a) => this.getTimeout(a.level) > 0) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not using |
||
| if (evictIdx !== -1) { | ||
| this.dismiss(this.alerts[evictIdx].id) | ||
| } else if (this.queue.length > 0 && this.alerts.length > 0) { | ||
| this.dismiss(this.alerts[0].id) | ||
| } | ||
| }, DRAIN_INTERVAL_MS) | ||
| }, | ||
|
sourcery-ai[bot] marked this conversation as resolved.
|
||
| stopDrain() { | ||
| if (this.drainTimer) { | ||
| clearInterval(this.drainTimer) | ||
| this.drainTimer = null | ||
| } | ||
| }, | ||
| alertType(level: MessageLevel): string { | ||
| switch (level) { | ||
| case MessageLevel.Success: | ||
| return 'success' | ||
| case MessageLevel.Error: | ||
| case MessageLevel.Critical: | ||
| return 'error' | ||
| case MessageLevel.Info: | ||
| return 'info' | ||
| case MessageLevel.Warning: | ||
| return 'warning' | ||
| case MessageLevel.Critical: | ||
| return 'critical' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why critical moved to error ? |
||
| default: | ||
| return 'info' | ||
| } | ||
| }, | ||
| timeout(): number { | ||
| switch (this.level) { | ||
| getTimeout(level: MessageLevel): number { | ||
| switch (level) { | ||
| case MessageLevel.Success: | ||
| case MessageLevel.Info: | ||
| return 5000 | ||
|
|
@@ -61,12 +132,21 @@ export default Vue.extend({ | |
| } | ||
| }, | ||
| }, | ||
| mounted() { | ||
| message_manager.addCallback((level: MessageLevel, message: string) => { | ||
| this.level = level | ||
| this.message = message | ||
| this.show = true | ||
| }) | ||
| }, | ||
| }) | ||
| </script> | ||
|
|
||
| <style scoped> | ||
| .alerter-stack { | ||
| position: fixed; | ||
| bottom: 16px; | ||
| left: 50%; | ||
| transform: translateX(-50%); | ||
| z-index: 1000; | ||
| min-width: 344px; | ||
| max-width: 672px; | ||
| } | ||
|
|
||
| .alerter-item { | ||
| margin-bottom: 8px; | ||
| } | ||
| </style> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not having it inside data ?