207 lines
7.7 KiB
Vue
207 lines
7.7 KiB
Vue
<script setup lang="ts">
|
|
import { user } from '@/composables/useFirebase'
|
|
import { encryptionKey, enableEncryption, disableEncryption } from '@/composables/useEncryption'
|
|
import { notes } from '@/composables/useNotes'
|
|
import { format } from 'date-fns'
|
|
import JSZip from 'jszip'
|
|
import FileSaver from 'file-saver'
|
|
|
|
const verificationEmailSent = ref(false)
|
|
const sendVerificationMail = () => {
|
|
if (!user.value) throw Error("User doesn't exist, can't send verification email")
|
|
user.value.sendEmailVerification()
|
|
verificationEmailSent.value = true
|
|
}
|
|
|
|
const exportNotes = async () => {
|
|
const zip = new JSZip()
|
|
notes.value.forEach((note) => {
|
|
zip.file(`${note.title}-${note.id}.md`, note.content)
|
|
})
|
|
const blob = await zip.generateAsync({ type: 'blob' })
|
|
const currentDate = format(new Date(), 'yyyyMMdd')
|
|
FileSaver.saveAs(blob, `contexted-${user.value?.email}-${currentDate}.zip`)
|
|
}
|
|
|
|
const showDeleteAccountDialog = ref(false)
|
|
const deleteAccount = async () => {
|
|
await user.value?.delete()
|
|
}
|
|
|
|
const showEncryptionDialog = ref(false)
|
|
watch(showEncryptionDialog, () => {
|
|
passphrase.value = ''
|
|
})
|
|
const passphrase = ref('')
|
|
const toggleEncryptionError = ref('')
|
|
|
|
const encryptionEnabled = computed(() => Boolean(encryptionKey.value))
|
|
|
|
const toggleEncryption = async () => {
|
|
const result = encryptionEnabled.value
|
|
? await disableEncryption(passphrase.value)
|
|
: await enableEncryption(passphrase.value)
|
|
if (typeof result === 'string') {
|
|
toggleEncryptionError.value = result
|
|
} else {
|
|
toggleEncryptionError.value = ''
|
|
showEncryptionDialog.value = false
|
|
}
|
|
}
|
|
</script>
|
|
<template>
|
|
<UIModal size="lg">
|
|
<template #activator="{ open }">
|
|
<UIDropdownItem @click="open">
|
|
<i class="fa-fw fa-solid fa-sliders" />
|
|
Account settings
|
|
</UIDropdownItem>
|
|
</template>
|
|
<template #title>
|
|
<i class="fa-fw fa-solid fa-sliders mr-2" />
|
|
Account settings
|
|
</template>
|
|
<template #default>
|
|
<div class="space-y-2">
|
|
<UICard>
|
|
<template #title>Account</template>
|
|
<template #default>
|
|
<template v-if="user?.email">
|
|
<div class="w-full flex-row sm:flex">
|
|
<div class="font-bold sm:w-4/12">E-mail address</div>
|
|
<div>{{ user.email }}</div>
|
|
</div>
|
|
<div class="w-full flex-row sm:flex">
|
|
<div class="font-bold sm:w-4/12">Account status</div>
|
|
<div class="col-auto">
|
|
<UIBadge :color="user.emailVerified ? 'success' : 'warning'">
|
|
{{ user.emailVerified ? 'Verified' : 'Not yet verified' }}
|
|
</UIBadge>
|
|
<UIButton
|
|
size="sm"
|
|
class="ml-2"
|
|
@click="sendVerificationMail"
|
|
v-if="!user.emailVerified"
|
|
:disabled="verificationEmailSent"
|
|
>
|
|
{{
|
|
verificationEmailSent
|
|
? 'Verification email sent'
|
|
: 'Request new verification email'
|
|
}}
|
|
</UIButton>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="w-full flex-row sm:flex">
|
|
<div class="font-bold sm:w-4/12">Account creation date</div>
|
|
<div>
|
|
{{ format(Date.parse(user?.metadata?.creationTime || ''), 'dd/MM/yyyy') }}
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</UICard>
|
|
<UICard>
|
|
<template #title>Notes</template>
|
|
<template #default>
|
|
<div class="items-top w-full flex-row sm:flex">
|
|
<div class="font-bold sm:w-4/12">Export notes</div>
|
|
<UIButton size="sm" @click="exportNotes">
|
|
<i class="fa-fw fa-solid fa-file-export"></i>
|
|
Export notes
|
|
</UIButton>
|
|
</div>
|
|
<div class="items-top w-full flex-row sm:flex sm:flex-grow">
|
|
<div class="flex-shrink-0 font-bold sm:w-4/12">Delete account</div>
|
|
<div>
|
|
<UIButton size="sm" color="error" @click="showDeleteAccountDialog = true">
|
|
<i class="fa-fw fa-solid fa-trash"></i>
|
|
Delete account
|
|
</UIButton>
|
|
<UIAlert
|
|
color="warning"
|
|
density="compact"
|
|
class="mt-1 space-y-2 text-sm"
|
|
v-if="showDeleteAccountDialog"
|
|
>
|
|
<div>
|
|
Are you sure you want to delete your Contexted account? This action cannot be
|
|
undone!
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<UIButton size="sm" variant="outline" color="primary" @click="deleteAccount">
|
|
Delete account
|
|
</UIButton>
|
|
<UIButton size="sm" variant="outline" @click="showDeleteAccountDialog = false">
|
|
Cancel
|
|
</UIButton>
|
|
</div>
|
|
</UIAlert>
|
|
</div>
|
|
</div>
|
|
<div class="items-top w-full flex-row sm:flex">
|
|
<div class="flex-shrink-0 font-bold sm:w-4/12">End-to-end encryption</div>
|
|
<div class="w-full">
|
|
<template v-if="!encryptionEnabled">
|
|
<UIButton
|
|
size="sm"
|
|
@click="showEncryptionDialog = true"
|
|
v-if="showEncryptionDialog === false"
|
|
>
|
|
<i class="fa-fw fa-solid fa-key"></i>
|
|
Enable end-to-end encryption
|
|
</UIButton>
|
|
</template>
|
|
<template v-else>
|
|
<UIButton
|
|
size="sm"
|
|
@click="showEncryptionDialog = true"
|
|
v-if="showEncryptionDialog === false"
|
|
>
|
|
<i class="fa-fw fa-solid fa-key"></i>
|
|
Disable end-to-end encryption
|
|
</UIButton>
|
|
</template>
|
|
<UIAlert color="info" density="compact" class="text-sm" v-if="showEncryptionDialog">
|
|
<div class="w-full space-y-2">
|
|
<div>
|
|
Enter your passphrase to
|
|
{{ encryptionEnabled ? 'disable' : 'enable' }}
|
|
encryption
|
|
</div>
|
|
<UIInputText
|
|
size="sm"
|
|
type="password"
|
|
:color="toggleEncryptionError ? 'error' : 'regular'"
|
|
v-model="passphrase"
|
|
class="w-full !max-w-full"
|
|
/>
|
|
<UIAlert density="compact" color="error" v-if="toggleEncryptionError">
|
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
|
{{ toggleEncryptionError }}
|
|
</UIAlert>
|
|
<div class="flex flex-wrap gap-2">
|
|
<UIButton
|
|
:disabled="passphrase.length === 0"
|
|
size="sm"
|
|
variant="outline"
|
|
color="primary"
|
|
@click="toggleEncryption"
|
|
>
|
|
{{ encryptionEnabled ? 'Disable' : 'Enable' }} encryption
|
|
</UIButton>
|
|
<UIButton size="sm" variant="outline" @click="showEncryptionDialog = false">
|
|
Cancel
|
|
</UIButton>
|
|
</div>
|
|
</div>
|
|
</UIAlert>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</UICard>
|
|
</div>
|
|
</template>
|
|
</UIModal>
|
|
</template>
|