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

[Desktop] Startup maintenance screen #2253

Merged
merged 65 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
bbe9335
Add RefreshButton component
webfiltered Jan 1, 2025
d997e8d
Add TS type - VueSeverity
webfiltered Jan 1, 2025
6691c4f
[Desktop] Add maintenance / validation view
webfiltered Jan 3, 2025
c21bd73
Add severity to task button confirmation
webfiltered Jan 10, 2025
74a6718
Add uv and VC redist tasks
webfiltered Jan 10, 2025
7f0d918
Allow 3 cards wide
webfiltered Jan 10, 2025
cfa3137
Add confirm text to destructive tasks, fix UX
webfiltered Jan 10, 2025
fd17a98
Add status toasts
webfiltered Jan 10, 2025
c0b865d
Always allow console access on maint. page
webfiltered Jan 15, 2025
0f7e739
Convert MaintenaceView to BaseViewTemplate
webfiltered Jan 15, 2025
1af9e3c
Add final maintenance tasks
webfiltered Jan 15, 2025
fa5b4d8
Add detailed description popover to task list
webfiltered Jan 15, 2025
2b22848
nit
webfiltered Jan 15, 2025
5e5293a
nit
webfiltered Jan 15, 2025
8fd1a79
Rename for clarity
webfiltered Jan 15, 2025
1724405
[Refactor] Use code-first i18n
webfiltered Jan 18, 2025
aed3102
nit - Remove postcss lang directive
webfiltered Jan 19, 2025
612d2db
Add error description for python packages
webfiltered Jan 19, 2025
f0b0ddf
Convert CSS to scoped styles
webfiltered Jan 19, 2025
fb1496c
nit - Remove unused code
webfiltered Jan 19, 2025
20a4fc2
[Refactor] Split tasks out to separate file
webfiltered Jan 19, 2025
07394ad
nit
webfiltered Jan 19, 2025
b874457
Fix regression in CSS from scoped refactor
webfiltered Jan 19, 2025
5366997
[Refactor] Simplify code
webfiltered Jan 19, 2025
051482b
[Refactor] Simplify code
webfiltered Jan 19, 2025
aeb49af
[Refactor] Tasks into composable-like
webfiltered Jan 19, 2025
c26ade8
Add typeRoots workaround
webfiltered Jan 19, 2025
201a343
Fix TS types
webfiltered Jan 19, 2025
147ba7f
Force i18n update
webfiltered Jan 19, 2025
08c3baf
[Refactor] customRef to VueUse
webfiltered Jan 20, 2025
9b32bf8
Fix TS types
webfiltered Jan 20, 2025
036996b
Rename for clarity - useMaintenanceTasks
webfiltered Jan 20, 2025
13a6bcf
Rename for clarity - task description fields
webfiltered Jan 20, 2025
9f7a487
Remove unused code
webfiltered Jan 20, 2025
4b58d96
nit - Doc
webfiltered Jan 20, 2025
9eeb40f
Fix TS type
webfiltered Jan 20, 2025
0e817bc
[Rename] maintenanceTasks.ts
webfiltered Jan 20, 2025
9f008d4
[Refactor] Move tasks back to static object
webfiltered Jan 21, 2025
3827e25
nit - Remove redundant code
webfiltered Jan 21, 2025
a09c3e8
Fix min duration ref always true on load
webfiltered Jan 21, 2025
b2ae493
Fix error description flash when loading
webfiltered Jan 21, 2025
e84c512
nit
webfiltered Jan 21, 2025
8a04677
Convert maintenance tasks to pinia store
webfiltered Jan 21, 2025
6eea54e
Update locales [skip ci]
invalid-email-address Jan 21, 2025
a6568fd
Re-impl. toast on execution fail
webfiltered Jan 21, 2025
a1f6342
nit
webfiltered Jan 21, 2025
158004e
Merge branch 'desktop-maintenance-screen' of https://github.com/Comfy…
webfiltered Jan 21, 2025
736c4fd
[SoC] Move messaging out of store
webfiltered Jan 21, 2025
b807fce
Update locales [skip ci]
invalid-email-address Jan 21, 2025
579c721
[i18n] Add maintenance task lobe parsing
webfiltered Jan 21, 2025
0837119
Merge branch 'desktop-maintenance-screen' of https://github.com/Comfy…
webfiltered Jan 21, 2025
34ff27b
[Refactor] Move readonly flags to "as const"
webfiltered Jan 21, 2025
13a0c9b
nit - Remove unused code
webfiltered Jan 21, 2025
f258344
Add TS type assertion to resolve TS strict errors
webfiltered Jan 21, 2025
7f230e5
nit - Doc, reorder for clarity
webfiltered Jan 21, 2025
f6f9d51
[Refactor] Simplify code
webfiltered Jan 21, 2025
482ac03
[Refactor] Move executing delay to components (SoC)
webfiltered Jan 21, 2025
f220bf4
[Refactor] Move refresh delay to components (SoC)
webfiltered Jan 21, 2025
46704d3
[Refactor] Move refresh delay to view (SoC)
webfiltered Jan 21, 2025
2351c6a
Revert "[i18n] Add maintenance task lobe parsing"
webfiltered Jan 21, 2025
d81087d
nit - Remove redundant code
webfiltered Jan 21, 2025
f1a9cfb
[Refactor] Change resolved bool flag to state
webfiltered Jan 21, 2025
ea13cd3
nit - Fix interface
webfiltered Jan 21, 2025
a288cce
nit
webfiltered Jan 21, 2025
2b6eb13
Rename for clarity - loading -> refreshing
webfiltered Jan 21, 2025
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
53 changes: 53 additions & 0 deletions src/components/common/RefreshButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<!--
A refresh button that disables and shows a progress spinner whilst active.

Usage:
```vue
<RefreshButton
v-model="isRefreshing"
:outlined="false"
@refresh="refresh"
/>
```
-->
<template>
<Button
class="relative p-button-icon-only"
:outlined="props.outlined"
:severity="props.severity"
:disabled="active || props.disabled"
@click="(event) => $emit('refresh', event)"
>
<span
class="p-button-icon pi pi-refresh transition-all"
:class="{ 'opacity-0': active }"
data-pc-section="icon"
></span>
<span class="p-button-label" data-pc-section="label">&nbsp;</span>
<ProgressSpinner v-show="active" class="absolute w-1/2 h-1/2" />
</Button>
</template>

<script setup lang="ts">
import Button from 'primevue/button'
import ProgressSpinner from 'primevue/progressspinner'

import { VueSeverity } from '@/types/primeVueTypes'

// Properties
interface Props {
outlined?: boolean
disabled?: boolean
severity?: VueSeverity
}
const props = withDefaults(defineProps<Props>(), {
outlined: true,
severity: 'secondary'
})

// Model
const active = defineModel<boolean>({ required: true })

// Emits
defineEmits(['refresh'])
</script>
40 changes: 40 additions & 0 deletions src/components/maintenance/StatusTag.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<Tag :icon :severity :value />
</template>

<script setup lang="ts">
import { PrimeIcons, type PrimeIconsOptions } from '@primevue/core/api'
import Tag, { TagProps } from 'primevue/tag'
import { ref, watch } from 'vue'

import { t } from '@/i18n'

// Properties
const props = defineProps<{
error: boolean
refreshing?: boolean
}>()

// Bindings
const icon = ref<string>(null)
const severity = ref<TagProps['severity']>(null)
const value = ref<PrimeIconsOptions[keyof PrimeIconsOptions]>(null)

const updateBindings = () => {
if (props.refreshing) {
icon.value = PrimeIcons.QUESTION
severity.value = 'info'
value.value = t('maintenance.refreshing')
} else if (props.error) {
icon.value = PrimeIcons.TIMES
severity.value = 'danger'
value.value = t('g.error')
} else {
icon.value = PrimeIcons.CHECK
severity.value = 'success'
value.value = t('maintenance.OK')
}
}

watch(props, updateBindings, { deep: true })
</script>
127 changes: 127 additions & 0 deletions src/components/maintenance/TaskCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<template>
<div
class="task-div max-w-48 min-h-52 grid relative"
:class="{ 'opacity-75': isLoading }"
>
<Card
class="max-w-48 relative h-full overflow-hidden"
:class="{ 'opacity-65': state.state !== 'error' }"
v-bind="(({ onClick, ...rest }) => rest)($attrs)"
>
<template #header>
<i
v-if="state.state === 'error'"
class="pi pi-exclamation-triangle text-red-500 absolute m-2 top-0 -right-14 opacity-15"
style="font-size: 10rem"
/>
<img
v-if="task.headerImg"
:src="task.headerImg"
class="object-contain w-full h-full opacity-25 pt-4 px-4"
/>
</template>
<template #title>{{ task.name }}</template>
<template #content>{{ description }}</template>
<template #footer>
<div class="flex gap-4 mt-1">
<Button
:icon="task.button?.icon"
:label="task.button?.text"
class="w-full"
raised
icon-pos="right"
@click="(event) => $emit('execute', event)"
:loading="isExecuting"
/>
</div>
</template>
</Card>

<i
v-if="!isLoading && state.state === 'OK'"
class="task-card-ok pi pi-check"
/>
</div>
</template>

<script setup lang="ts">
import Button from 'primevue/button'
import Card from 'primevue/card'
import { computed } from 'vue'

import { useMaintenanceTaskStore } from '@/stores/maintenanceTaskStore'
import type { MaintenanceTask } from '@/types/desktop/maintenanceTypes'
import { useMinLoadingDurationRef } from '@/utils/refUtil'

const taskStore = useMaintenanceTaskStore()
const state = computed(() => taskStore.getState(props.task))

// Properties
const props = defineProps<{
task: MaintenanceTask
}>()

// Events
defineEmits<{
execute: [event: MouseEvent]
}>()

// Bindings
const description = computed(() =>
state.value.state === 'error'
? props.task.errorDescription ?? props.task.shortDescription
: props.task.shortDescription
)

// Use a minimum run time to ensure tasks "feel" like they have run
const reactiveLoading = computed(() => state.value.refreshing)
const reactiveExecuting = computed(() => state.value.executing)

const isLoading = useMinLoadingDurationRef(reactiveLoading, 250)
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250)
</script>

<style scoped>
.task-card-ok {
@apply text-green-500 absolute -right-4 -bottom-4 opacity-100 row-span-full col-span-full transition-opacity;

font-size: 4rem;
text-shadow: 0.25rem 0 0.5rem black;
z-index: 10;
}

.p-card {
@apply transition-opacity;

--p-card-background: var(--p-button-secondary-background);
opacity: 0.9;

&.opacity-65 {
opacity: 0.4;
}

&:hover {
opacity: 1;
}
}

:deep(.p-card-header) {
z-index: 0;
}

:deep(.p-card-body) {
z-index: 1;
flex-grow: 1;
justify-content: space-between;
}

.task-div {
> i {
pointer-events: none;
}

&:hover > i {
opacity: 0.2;
}
}
</style>
86 changes: 86 additions & 0 deletions src/components/maintenance/TaskListItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template>
<tr
class="border-neutral-700 border-solid border-y"
:class="{
'opacity-50': state.state === 'resolved',
'opacity-75': isLoading && state.state !== 'resolved'
}"
>
<td class="text-center w-16">
<TaskListStatusIcon :state="state.state" :loading="isLoading" />
</td>
<td>
<p class="inline-block">{{ task.name }}</p>
<Button
class="inline-block mx-2"
type="button"
:icon="PrimeIcons.INFO_CIRCLE"
severity="secondary"
:text="true"
@click="toggle"
/>

<Popover ref="infoPopover" class="block m-1 max-w-64 min-w-32">
<span class="whitespace-pre-line">{{ task.description }}</span>
</Popover>
</td>
<td class="text-right px-4">
<Button
:icon="task.button?.icon"
:label="task.button?.text"
:severity
icon-pos="right"
@click="(event) => $emit('execute', event)"
:loading="isExecuting"
/>
</td>
</tr>
</template>

<script setup lang="ts">
import { PrimeIcons } from '@primevue/core/api'
import Button from 'primevue/button'
import Popover from 'primevue/popover'
import { computed, ref } from 'vue'

import { useMaintenanceTaskStore } from '@/stores/maintenanceTaskStore'
import type { MaintenanceTask } from '@/types/desktop/maintenanceTypes'
import { VueSeverity } from '@/types/primeVueTypes'
import { useMinLoadingDurationRef } from '@/utils/refUtil'

import TaskListStatusIcon from './TaskListStatusIcon.vue'

const taskStore = useMaintenanceTaskStore()
const state = computed(() => taskStore.getState(props.task))

// Properties
const props = defineProps<{
task: MaintenanceTask
}>()

// Events
defineEmits<{
execute: [event: MouseEvent]
}>()

// Binding
const severity = computed<VueSeverity>(() =>
state.value.state === 'error' || state.value.state === 'warning'
? 'primary'
: 'secondary'
)

// Use a minimum run time to ensure tasks "feel" like they have run
const reactiveLoading = computed(() => state.value.refreshing)
const reactiveExecuting = computed(() => state.value.executing)

const isLoading = useMinLoadingDurationRef(reactiveLoading, 250)
const isExecuting = useMinLoadingDurationRef(reactiveExecuting, 250)

// Popover
const infoPopover = ref()

const toggle = (event: Event) => {
infoPopover.value.toggle(event)
}
</script>
Loading
Loading