Files
dbx/apps/desktop/src/components/connection/ReadOnlySessionControl.vue
T

74 lines
2.9 KiB
Vue

<script setup lang="ts">
import { computed, onMounted, watch } from "vue";
import { useI18n } from "vue-i18n";
import { Lock, Unlock } from "@lucide/vue";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useConnectionStore } from "@/stores/connectionStore";
import { useReadOnlyUnlockStore } from "@/stores/readOnlyUnlockStore";
import { formatWriteUnlockRemaining } from "@/lib/database/readOnlyWriteAccess";
const props = defineProps<{
connectionId: string;
compact?: boolean;
showLabel?: boolean;
}>();
const { t } = useI18n();
const connectionStore = useConnectionStore();
const unlockStore = useReadOnlyUnlockStore();
const persistentlyReadOnly = computed(() => connectionStore.getConfig(props.connectionId)?.read_only === true);
const remainingMs = computed(() => unlockStore.remainingMs(props.connectionId));
const unlocked = computed(() => remainingMs.value > 0);
const remainingLabel = computed(() => formatWriteUnlockRemaining(remainingMs.value));
const tooltip = computed(() => {
if (unlocked.value) return `${t("readOnlyUnlock.unlockedBadge")} · ${t("readOnlyUnlock.remaining", { time: remainingLabel.value })}`;
return t("connection.readOnlyBadge");
});
async function onActivate() {
if (!persistentlyReadOnly.value) return;
if (unlocked.value) {
await unlockStore.lockNow(props.connectionId);
return;
}
await unlockStore.requestUnlock({
connectionId: props.connectionId,
connectionName: connectionStore.getConfig(props.connectionId)?.name,
source: t("readOnlyUnlock.sourceStatus"),
});
}
watch(
() => props.connectionId,
(connectionId) => {
if (persistentlyReadOnly.value) void unlockStore.refreshFromBackend(connectionId);
},
);
onMounted(() => {
if (persistentlyReadOnly.value) void unlockStore.refreshFromBackend(props.connectionId);
});
</script>
<template>
<Tooltip v-if="persistentlyReadOnly">
<TooltipTrigger as-child>
<button
type="button"
class="inline-flex items-center gap-0.5 rounded text-muted-foreground hover:bg-muted-foreground/15 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
:class="compact ? 'h-4 px-0.5' : 'h-4 px-1'"
:aria-label="unlocked ? t('readOnlyUnlock.lockNow') : t('readOnlyUnlock.unlockAction')"
:title="unlocked ? t('readOnlyUnlock.lockNow') : t('readOnlyUnlock.unlockAction')"
@click.stop.prevent="onActivate"
>
<Unlock v-if="unlocked" class="h-3 w-3 shrink-0 text-amber-600 dark:text-amber-400" />
<Lock v-else class="h-3 w-3 shrink-0" />
<span v-if="showLabel && !unlocked" class="text-[10px] leading-none">{{ t("connection.readOnlyBadge") }}</span>
<span v-if="unlocked" class="text-[10px] tabular-nums leading-none">{{ remainingLabel }}</span>
</button>
</TooltipTrigger>
<TooltipContent>{{ tooltip }}</TooltipContent>
</Tooltip>
</template>