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

Replace SWALs with Interactive Dialogs #1161

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ jobs:
bun run lint
bun run typecheck
bun run build
xvfb-run --auto-servernum bun test:ci

deploy-page:
needs: test
Expand Down
17 changes: 0 additions & 17 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@
<script setup lang="ts">
import { onClickOutside, useDebounceFn, useFullscreen, useTimestamp, useWindowSize } from '@vueuse/core'
import { format } from 'date-fns'
import Swal from 'sweetalert2'
import { computed, DefineComponent, markRaw, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'

Expand Down Expand Up @@ -448,8 +447,6 @@ const mainMenuWidth = computed(() => {
return { width }
})

let requestDisarmConfirmationPopup = false

const toggleMainMenu = (): void => {
if (isMenuOpen.value === true) {
closeMainMenu()
Expand All @@ -458,8 +455,6 @@ const toggleMainMenu = (): void => {
}
}

// When a isVehicleArmed change its value a watcher call Swal.close, this flag is
// used to avoid closing others Swal instances instead of the one intended
const openMainMenu = (): void => {
if (vehicleStore.isArmed) {
showDialog({
Expand Down Expand Up @@ -493,7 +488,6 @@ const openMainMenu = (): void => {
showMainMenu.value = true
isMenuOpen.value = true
}
requestDisarmConfirmationPopup = false
})
} else {
showMainMenu.value = true
Expand Down Expand Up @@ -555,13 +549,6 @@ const menuLabelSize = computed(() => {
return 'text-[10px]'
})

const isVehicleArmed = computed(() => vehicleStore.isArmed)
watch(isVehicleArmed, (isArmed) => {
if (requestDisarmConfirmationPopup && !isArmed) {
Swal.close()
}
})

const mainMenu = ref()
onClickOutside(mainMenu, () => {
if (mainMenuStep.value === 1) {
Expand Down Expand Up @@ -732,10 +719,6 @@ body.hide-cursor {
top: -11%;
}

.swal2-container {
z-index: 10000;
}

.fade-enter-active,
.fade-leave-active {
transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
Expand Down
32 changes: 23 additions & 9 deletions src/components/EditMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,6 @@

<script setup lang="ts">
import { useConfirmDialog } from '@vueuse/core'
import Swal from 'sweetalert2'
import { v4 as uuid } from 'uuid'
import { computed, onMounted, ref, toRefs, watch } from 'vue'
import { nextTick } from 'vue'
Expand Down Expand Up @@ -789,7 +788,11 @@ const renameView = (view: View): void => {

const toggleViewVisibility = (view: View): void => {
if (view.visible && view === store.currentView) {
Swal.fire({ text: 'You cannot hide the current view.', icon: 'error' })
showDialog({
message: 'You cannot hide the current view.',
variant: 'error',
maxWidth: 400,
})
return
}
view.visible = !view.visible
Expand All @@ -801,14 +804,25 @@ const renameProfile = (profile: Profile): void => {
profileConfigDialogRevealed.value = true
}

const resetSavedProfiles = async (): Promise<void> => {
const result = await Swal.fire({
text: 'Are you sure you want to reset your profiles to the default ones?',
showCancelButton: true,
confirmButtonText: 'Reset',
icon: 'warning',
const resetSavedProfiles = (): void => {
showDialog({
message: 'Are you sure you want to reset your profiles to the default ones?',
actions: [
{
text: 'cancel',
action: () => {
closeDialog()
},
},
{
text: 'reset profiles',
action: () => {
store.resetSavedProfiles()
},
},
],
variant: 'warning',
})
if (result.isConfirmed) store.resetSavedProfiles()
}

const availableWidgetsContainer = ref()
Expand Down
63 changes: 55 additions & 8 deletions src/components/InteractionDialog.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<v-dialog v-model="internalShowDialog" :persistent="persistent" :width="maxWidth || '600px'">
<v-dialog v-model="internalShowDialog" :persistent="persistent" :width="maxWidth || 'auto'">
<v-card
:width="maxWidth || '600px'"
:width="maxWidth || 'auto'"
class="main-dialog px-2 rounded-lg"
:style="interfaceStore.globalGlassMenuStyles"
>
Expand Down Expand Up @@ -36,8 +36,16 @@
<component :is="contentComponent"></component>
</template>
</v-card-text>
<v-progress-linear
v-if="props.timer"
:color="props.variant === 'error' ? 'red' : 'green'"
striped
:model-value="timerCounter"
height="5"
class="w-full"
/>
<div class="flex justify-center w-full px-10">
<v-divider class="opacity-10 border-[#fafafa]"></v-divider>
<v-divider v-if="!props.timer" class="opacity-10 border-[#fafafa]"></v-divider>
</div>
<v-card-actions>
<slot v-if="hasActionsSlot" name="actions"></slot>
Expand Down Expand Up @@ -72,7 +80,7 @@
</template>

<script setup lang="ts">
import { computed, ref, useSlots, watch } from 'vue'
import { computed, onMounted, onUnmounted, ref, useSlots, watch } from 'vue'

import { useInteractionDialog } from '@/composables/interactionDialog'
import { useAppInterfaceStore } from '@/stores/appInterface'
Expand Down Expand Up @@ -148,24 +156,30 @@ interface Props {
* Persistent dialogs can't be closed with 'esc' or a backdrop click.
*/
persistent?: boolean
/**
*
*/
timer?: number
}

const props = withDefaults(defineProps<Props>(), {
showDialog: false,
title: '',
contentComponent: '',
maxWidth: 600,
maxWidth: 'auto',
actions: () => [],
variant: 'info',
message: '',
persistent: false,
timer: 0,
})

const emit = defineEmits(['update:showDialog', 'confirmed', 'dismissed'])

const internalShowDialog = ref(props.showDialog)

const isArrayMessage = computed(() => Array.isArray(props.message))
const timerCounter = ref(100)
const timerId = ref<number | null>(null)

const iconType = computed(() => {
switch (props.variant) {
Expand All @@ -184,7 +198,6 @@ const iconColor = computed(() => {
return props.variant === 'success' ? 'green' : 'yellow'
})

// Check if the actions slot has been provided
const hasActionsSlot = computed(() => !!slots.actions)

watch(
Expand All @@ -194,11 +207,45 @@ watch(
}
)

onMounted(() => {
if (props.timer) {
startTimer()
}
})

const startTimer = (): void => {
timerCounter.value = 100
const interval = 100
const step = (interval / props.timer) * 100

timerId.value = setInterval(() => {
timerCounter.value -= step
if (timerCounter.value <= 0) {
stopTimer()
handleAction(() => (internalShowDialog.value = false))
emit('dismissed')
}
}, interval) as unknown as number
}

const stopTimer = (): void => {
console.log('🚀 ~ stopTimer:')
if (timerId.value !== null) {
clearInterval(timerId.value)
timerId.value = null
}
}

onUnmounted(() => {
stopTimer()
})

watch(internalShowDialog, (newVal) => {
if (!newVal) {
closeDialog()
stopTimer()
emit('update:showDialog', newVal)
emit('dismissed')
closeDialog()
}
})

Expand Down
68 changes: 45 additions & 23 deletions src/components/mini-widgets/MiniVideoRecorder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@
</div>
</div>
<v-dialog v-model="widgetStore.miniWidgetManagerVars(miniWidget.hash).configMenuOpen" width="auto">
<div class="p-6 m-5 bg-white rounded-md">
<p class="text-xl font-semibold">Choose a stream to record</p>
<div class="w-auto h-px my-2 bg-grey-lighten-3" />
<div
class="flex flex-col items-center p-2 pt-1 m-5 rounded-md gap-y-4"
:style="interfaceStore.globalGlassMenuStyles"
>
<p class="text-xl font-semibold m-4">Choose a stream to record</p>
<v-select
:model-value="nameSelectedStream"
label="Stream name"
Expand All @@ -61,24 +63,28 @@
no-data-text="No streams available."
hide-details
return-object
theme="dark"
class="w-[90%]"
@update:model-value="updateCurrentStream"
/>
<div class="flex items-center">
<button
class="w-auto p-3 m-2 font-medium transition-all rounded-md shadow-md text-uppercase hover:bg-slate-100"
<div class="flex w-full justify-between items-center mt-4">
<v-btn
class="w-auto text-uppercase"
variant="text"
@click="widgetStore.miniWidgetManagerVars(miniWidget.hash).configMenuOpen = false"
>
Cancel
</button>
<button
class="flex items-center p-3 mx-2 font-medium transition-all rounded-md shadow-md w-fit text-uppercase hover:bg-slate-100"
:class="{ 'bg-slate-200 opacity-30 pointer-events-none': isLoadingStream }"
</v-btn>
<v-btn
class="bg-[#FFFFFF11] hover:bg-[#FFFFFF33]"
size="large"
:class="{ 'opacity-30 pointer-events-none': isLoadingStream }"
@click="startRecording"
>
<span>Record</span>
<v-icon v-if="isLoadingStream" class="m-2 animate-spin">mdi-loading</v-icon>
<div v-else class="w-5 h-5 ml-2 rounded-full bg-red" />
</button>
</v-btn>
</div>
</div>
</v-dialog>
Expand All @@ -89,16 +95,19 @@
import { useMouseInElement, useTimestamp } from '@vueuse/core'
import { intervalToDuration } from 'date-fns'
import { storeToRefs } from 'pinia'
import Swal from 'sweetalert2'
import { computed, onBeforeMount, onBeforeUnmount, onMounted, ref, toRefs, watch } from 'vue'

import { useInteractionDialog } from '@/composables/interactionDialog'
import { isEqual, sleep } from '@/libs/utils'
import { useAppInterfaceStore } from '@/stores/appInterface'
import { useVideoStore } from '@/stores/video'
import { useWidgetManagerStore } from '@/stores/widgetManager'
import type { MiniWidget } from '@/types/widgets'

import VideoLibrary from '../VideoLibraryModal.vue'

const { showDialog, closeDialog } = useInteractionDialog()
const interfaceStore = useAppInterfaceStore()
const widgetStore = useWidgetManagerStore()
const videoStore = useVideoStore()

Expand Down Expand Up @@ -158,14 +167,14 @@ function assertStreamIsSelectedAndAvailable(
nameSelectedStream.value = selectedStream

if (nameSelectedStream.value === undefined) {
Swal.fire({ text: 'No stream selected.', icon: 'error' })
showDialog({ message: 'No stream selected.', variant: 'error' })
return
}

if (namesAvailableStreams.value.includes(nameSelectedStream.value)) return

const errorMsg = `The selected stream is not available. Please check its source or select another stream.`
Swal.fire({ text: errorMsg, icon: 'error' })
showDialog({ message: errorMsg, variant: 'error' })
throw new Error(errorMsg)
}

Expand All @@ -189,7 +198,7 @@ const toggleRecording = async (): Promise<void> => {

const startRecording = (): void => {
if (nameSelectedStream.value && !videoStore.getStreamData(nameSelectedStream.value)?.connected) {
Swal.fire({ title: 'Cannot start recording.', text: 'Stream is not connected.', icon: 'error' })
showDialog({ title: 'Cannot start recording.', message: 'Stream is not connected.', variant: 'error' })
return
}
assertStreamIsSelectedAndAvailable(nameSelectedStream.value)
Expand Down Expand Up @@ -231,7 +240,7 @@ const updateCurrentStream = async (streamName: string | undefined): Promise<void
}

if (isLoadingStream.value) {
Swal.fire({ text: 'Could not load media stream.', icon: 'error' })
showDialog({ message: 'Could not load media stream.', variant: 'error' })
return
}

Expand All @@ -251,14 +260,27 @@ if (widgetStore.isRealMiniWidget(miniWidget.value)) {
if (namesAvailableStreams.value.length > 1) {
const text = `You have multiple streams available, so we chose one randomly to start with.
If you want to change it, please open the widget configuration.`
Swal.fire({ title: 'Multiple streams detected', text: text, icon: 'info', confirmButtonText: 'OK' })
showDialog({
maxWidth: 600,
title: 'Multiple streams detected',
message: text,
variant: 'info',
actions: [
{
text: 'Ok',
action: () => {
closeDialog()
},
},
],
})
}
}

const updatedMediaStream = videoStore.getMediaStream(miniWidget.value.options.streamName)
// If the widget is not connected to the MediaStream, try to connect it
if (!isEqual(updatedMediaStream, mediaStream.value)) {
mediaStream.value = updatedMediaStream
const updatedMediaStream = videoStore.getMediaStream(miniWidget.value.options.streamName)
// If the widget is not connected to the MediaStream, try to connect it
if (!isEqual(updatedMediaStream, mediaStream.value)) {
mediaStream.value = updatedMediaStream
}
}
}, 1000)
}
Expand Down Expand Up @@ -298,7 +320,7 @@ watch(isRecording, () => {
You have a video recording ongoing.
Remember to stop it before closing Cockpit, or the record will be lost.
`
Swal.fire({ text: alertMsg, icon: 'warning' })
showDialog({ message: alertMsg, variant: 'warning' })
return 'I hope the user does not click on the leave button.'
}
})
Expand Down
Loading
Loading