204 lines
6.8 KiB
Vue
204 lines
6.8 KiB
Vue
<script setup lang="ts">
|
|
import {
|
|
Dialog,
|
|
DialogTrigger,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
} from '@/components/ui/dialog';
|
|
import { useShareDialog } from '@/stores';
|
|
import { toTypedSchema } from '@vee-validate/yup';
|
|
import { useForm } from 'vee-validate';
|
|
import { toast } from 'vue-sonner';
|
|
import { createShare, listShares } from '~/lib/api/shares';
|
|
import { shareSchema } from '~/lib/schemas/share';
|
|
import type { Share } from '~/shared/types/shares';
|
|
|
|
const warrenStore = useWarrenStore();
|
|
const dialog = useShareDialog();
|
|
|
|
const newScreen = ref(false);
|
|
|
|
const existingShares = ref<Share[]>([]);
|
|
useAsyncData('current-file-shares', async () => {
|
|
if (warrenStore.current == null || dialog.target == null) {
|
|
return [];
|
|
}
|
|
|
|
const result = await listShares(
|
|
warrenStore.current.warrenId,
|
|
joinPaths(warrenStore.current.path, dialog.target.name)
|
|
);
|
|
|
|
if (!result.success) {
|
|
return [];
|
|
}
|
|
|
|
if (result.shares.length < 1) {
|
|
newScreen.value = true;
|
|
} else {
|
|
newScreen.value = false;
|
|
}
|
|
|
|
existingShares.value = result.shares;
|
|
});
|
|
dialog.$subscribe(async (_, value) => {
|
|
if (value.target !== null) {
|
|
await refreshNuxtData('current-file-shares');
|
|
}
|
|
});
|
|
|
|
function onOpenChange() {
|
|
dialog.reset();
|
|
}
|
|
|
|
function onCancel() {
|
|
if (newScreen.value && existingShares.value.length > 0) {
|
|
newScreen.value = false;
|
|
} else {
|
|
dialog.reset();
|
|
}
|
|
}
|
|
|
|
const form = useForm({
|
|
validationSchema: toTypedSchema(shareSchema),
|
|
});
|
|
|
|
const onSubmit = form.handleSubmit(async (values) => {
|
|
if (dialog.target == null || warrenStore.current == null) {
|
|
return;
|
|
}
|
|
|
|
const result = await createShare(
|
|
warrenStore.current.warrenId,
|
|
joinPaths(warrenStore.current.path, dialog.target.name),
|
|
values.password ?? null,
|
|
values.lifetime ?? null
|
|
);
|
|
|
|
if (result.success) {
|
|
toast.success('Share', {
|
|
description: `Successfully created a share for ${dialog.target.name}`,
|
|
});
|
|
await refreshNuxtData('current-file-shares');
|
|
newScreen.value = false;
|
|
} else {
|
|
toast.error('Share', {
|
|
description: `Failed to create share`,
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<Dialog :open="dialog.target != null" @update:open="onOpenChange">
|
|
<DialogTrigger as-child>
|
|
<slot />
|
|
</DialogTrigger>
|
|
<DialogContent
|
|
v-if="dialog.target != null"
|
|
class="w-full !max-w-[calc(100vw-8px)] sm:!max-w-[min(98vw,1000px)]"
|
|
>
|
|
<DialogHeader class="overflow-hidden">
|
|
<DialogTitle class="truncate"
|
|
>Share {{ dialog.target.name }}</DialogTitle
|
|
>
|
|
<DialogDescription
|
|
>Create a shareable link to this
|
|
{{ dialog.target.fileType }}</DialogDescription
|
|
>
|
|
</DialogHeader>
|
|
|
|
<SharesTable v-if="!newScreen" :shares="existingShares" />
|
|
<form
|
|
v-else
|
|
id="share-form"
|
|
class="grid gap-4"
|
|
@submit.prevent="onSubmit"
|
|
>
|
|
<FormField v-slot="{ componentField }" name="password">
|
|
<FormItem>
|
|
<FormLabel>Password</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
v-bind="componentField"
|
|
id="password"
|
|
type="password"
|
|
autocomplete="off"
|
|
data-1p-ignore
|
|
data-protonpass-ignore
|
|
data-bwignore
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
<FormDescription>
|
|
<span
|
|
v-if="
|
|
form.values.password == null ||
|
|
form.values.password.length < 1
|
|
"
|
|
>
|
|
The share will not require a password
|
|
</span>
|
|
<span v-else>
|
|
The share will require the specified password
|
|
</span>
|
|
</FormDescription>
|
|
</FormItem>
|
|
</FormField>
|
|
<FormField v-slot="{ componentField }" name="lifetime">
|
|
<FormItem>
|
|
<FormLabel>Lifetime (seconds)</FormLabel>
|
|
<FormControl>
|
|
<div
|
|
class="flex w-full flex-row justify-between gap-1"
|
|
>
|
|
<Input
|
|
v-bind="componentField"
|
|
id="lifetime"
|
|
type="number"
|
|
class="w-full max-w-full min-w-0"
|
|
autocomplete="off"
|
|
data-1p-ignore
|
|
data-protonpass-ignore
|
|
data-bwignore
|
|
/>
|
|
</div>
|
|
</FormControl>
|
|
<FormMessage />
|
|
<FormDescription>
|
|
<span
|
|
v-if="
|
|
form.values.lifetime != null &&
|
|
form.values.lifetime > 0
|
|
"
|
|
class="w-full"
|
|
>
|
|
The share will expire in
|
|
{{
|
|
$dayjs
|
|
.duration({
|
|
seconds: form.values.lifetime,
|
|
})
|
|
.humanize()
|
|
}}
|
|
</span>
|
|
<span v-else> The share will be permanent </span>
|
|
</FormDescription>
|
|
</FormItem>
|
|
</FormField>
|
|
</form>
|
|
|
|
<DialogFooter>
|
|
<Button variant="ghost" @click="onCancel">Cancel</Button>
|
|
<Button v-if="newScreen" type="submit" form="share-form"
|
|
>Share</Button
|
|
>
|
|
<Button v-else @click="() => (newScreen = true)">New</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</template>
|