apps/ui: Warp's design system for Vue 3 + Tailwind v4. Tokens, fonts, icons and logo come from the brand package, with a Tailwind theme on the tokens and Vue components on the brand component styles. apps/admin: the admin panel as a separate, optional service. warp-admin embeds the built Vue app and proxies only the login endpoints and /_warp/admin/v1 to the homeserver. Pages: dashboard, users, rooms, federation, registration and tokens, server notices, application services, audit log. Also: pnpm workspace, go.work entry, Dockerfile, an `admin` compose profile, Makefile targets, a CI job with a proxy smoke test, and docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
59 lines
1.4 KiB
TypeScript
59 lines
1.4 KiB
TypeScript
import { ref, shallowRef, type Ref } from 'vue'
|
|
import { ApiError } from '@/api/client'
|
|
import { errorMessage } from './format'
|
|
|
|
// A view's data: what was loaded, whether it is loading, and why it failed.
|
|
// A reload keeps the old data on screen until the new data is in.
|
|
|
|
export function useLoad<T>(fn: () => Promise<T>) {
|
|
const data = shallowRef<T>() as Ref<T | undefined>
|
|
const loading = ref(false)
|
|
const error = ref('')
|
|
const status = ref(0)
|
|
let seq = 0
|
|
|
|
async function load() {
|
|
const mine = ++seq
|
|
loading.value = true
|
|
error.value = ''
|
|
try {
|
|
const value = await fn()
|
|
if (mine === seq) {
|
|
data.value = value
|
|
status.value = 200
|
|
}
|
|
} catch (e) {
|
|
if (mine === seq) {
|
|
error.value = errorMessage(e)
|
|
status.value = e instanceof ApiError ? e.status : 0
|
|
}
|
|
} finally {
|
|
if (mine === seq) loading.value = false
|
|
}
|
|
}
|
|
|
|
return { data, loading, error, status, load }
|
|
}
|
|
|
|
/** Runs an action and tracks whether it is running; returns whether it worked. */
|
|
export function useAction() {
|
|
const busy = ref(false)
|
|
const error = ref('')
|
|
|
|
async function run(fn: () => Promise<unknown>): Promise<boolean> {
|
|
busy.value = true
|
|
error.value = ''
|
|
try {
|
|
await fn()
|
|
return true
|
|
} catch (e) {
|
|
error.value = errorMessage(e)
|
|
return false
|
|
} finally {
|
|
busy.value = false
|
|
}
|
|
}
|
|
|
|
return { busy, error, run }
|
|
}
|