mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
add desktop
This commit is contained in:
119
src/components/haex/desktop/icon.vue
Normal file
119
src/components/haex/desktop/icon.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<UContextMenu :items="contextMenuItems">
|
||||
<div
|
||||
ref="draggableEl"
|
||||
:style="style"
|
||||
class="select-none cursor-grab active:cursor-grabbing"
|
||||
@pointerdown="handlePointerDown"
|
||||
@pointermove="handlePointerMove"
|
||||
@pointerup="handlePointerUp"
|
||||
@dblclick="handleOpen"
|
||||
>
|
||||
<div class="flex flex-col items-center gap-1 p-2">
|
||||
<div
|
||||
class="w-16 h-16 flex items-center justify-center bg-white/90 dark:bg-gray-800/90 rounded-lg shadow-lg hover:shadow-xl transition-shadow"
|
||||
>
|
||||
<img v-if="icon" :src="icon" :alt="label" class="w-12 h-12 object-contain" />
|
||||
<Icon v-else name="i-heroicons-puzzle-piece-solid" class="w-12 h-12 text-gray-500" />
|
||||
</div>
|
||||
<span
|
||||
class="text-xs text-center max-w-20 truncate bg-white/80 dark:bg-gray-800/80 px-2 py-1 rounded shadow"
|
||||
>
|
||||
{{ label }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</UContextMenu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
id: string
|
||||
itemType: 'extension' | 'file' | 'folder'
|
||||
referenceId: string
|
||||
initialX: number
|
||||
initialY: number
|
||||
label: string
|
||||
icon?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
positionChanged: [id: string, x: number, y: number]
|
||||
open: [itemType: string, referenceId: string]
|
||||
uninstall: [itemType: string, referenceId: string]
|
||||
dragStart: [id: string, itemType: string, referenceId: string]
|
||||
dragEnd: []
|
||||
}>()
|
||||
|
||||
const desktopStore = useDesktopStore()
|
||||
|
||||
const contextMenuItems = computed(() =>
|
||||
desktopStore.getContextMenuItems(
|
||||
props.id,
|
||||
props.itemType,
|
||||
props.referenceId,
|
||||
handleOpen,
|
||||
handleUninstall,
|
||||
),
|
||||
)
|
||||
|
||||
const draggableEl = ref<HTMLElement>()
|
||||
const x = ref(props.initialX)
|
||||
const y = ref(props.initialY)
|
||||
const isDragging = ref(false)
|
||||
const offsetX = ref(0)
|
||||
const offsetY = ref(0)
|
||||
|
||||
const style = computed(() => ({
|
||||
position: 'absolute' as const,
|
||||
left: `${x.value}px`,
|
||||
top: `${y.value}px`,
|
||||
touchAction: 'none' as const,
|
||||
}))
|
||||
|
||||
const handlePointerDown = (e: PointerEvent) => {
|
||||
if (!draggableEl.value || !draggableEl.value.parentElement) return
|
||||
|
||||
isDragging.value = true
|
||||
emit('dragStart', props.id, props.itemType, props.referenceId)
|
||||
|
||||
// Get parent offset to convert from viewport coordinates to parent-relative coordinates
|
||||
const parentRect = draggableEl.value.parentElement.getBoundingClientRect()
|
||||
|
||||
// Calculate offset from mouse position to current element position (in parent coordinates)
|
||||
offsetX.value = e.clientX - parentRect.left - x.value
|
||||
offsetY.value = e.clientY - parentRect.top - y.value
|
||||
|
||||
draggableEl.value.setPointerCapture(e.pointerId)
|
||||
}
|
||||
|
||||
const handlePointerMove = (e: PointerEvent) => {
|
||||
if (!isDragging.value || !draggableEl.value?.parentElement) return
|
||||
|
||||
const parentRect = draggableEl.value.parentElement.getBoundingClientRect()
|
||||
const newX = e.clientX - parentRect.left - offsetX.value
|
||||
const newY = e.clientY - parentRect.top - offsetY.value
|
||||
|
||||
x.value = newX
|
||||
y.value = newY
|
||||
}
|
||||
|
||||
const handlePointerUp = (e: PointerEvent) => {
|
||||
if (!isDragging.value) return
|
||||
|
||||
isDragging.value = false
|
||||
if (draggableEl.value) {
|
||||
draggableEl.value.releasePointerCapture(e.pointerId)
|
||||
}
|
||||
emit('dragEnd')
|
||||
emit('positionChanged', props.id, x.value, y.value)
|
||||
}
|
||||
|
||||
const handleOpen = () => {
|
||||
emit('open', props.itemType, props.referenceId)
|
||||
}
|
||||
|
||||
const handleUninstall = () => {
|
||||
emit('uninstall', props.itemType, props.referenceId)
|
||||
}
|
||||
</script>
|
||||
250
src/components/haex/desktop/index.vue
Normal file
250
src/components/haex/desktop/index.vue
Normal file
@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div
|
||||
class="w-full h-full relative overflow-hidden bg-gradient-to-br from-blue-50 to-blue-100 dark:from-gray-900 dark:to-gray-800"
|
||||
>
|
||||
<!-- Dropzones (only visible during drag) -->
|
||||
<Transition name="slide-down">
|
||||
<div
|
||||
v-if="isDragging"
|
||||
class="absolute top-0 left-0 right-0 flex gap-2 p-4 z-50"
|
||||
>
|
||||
<!-- Remove from Desktop Dropzone -->
|
||||
<div
|
||||
ref="removeDropzoneEl"
|
||||
class="flex-1 h-20 flex items-center justify-center gap-2 rounded-lg border-2 border-dashed transition-all"
|
||||
:class="
|
||||
isOverRemoveZone
|
||||
? 'bg-orange-500/20 border-orange-500 dark:bg-orange-400/20 dark:border-orange-400'
|
||||
: 'border-orange-500/50 dark:border-orange-400/50'
|
||||
"
|
||||
>
|
||||
<Icon
|
||||
name="i-heroicons-x-mark"
|
||||
class="w-6 h-6"
|
||||
:class="
|
||||
isOverRemoveZone
|
||||
? 'text-orange-700 dark:text-orange-300'
|
||||
: 'text-orange-600 dark:text-orange-400'
|
||||
"
|
||||
/>
|
||||
<span
|
||||
class="font-semibold"
|
||||
:class="
|
||||
isOverRemoveZone
|
||||
? 'text-orange-700 dark:text-orange-300'
|
||||
: 'text-orange-600 dark:text-orange-400'
|
||||
"
|
||||
>
|
||||
Von Desktop entfernen
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Uninstall Dropzone -->
|
||||
<div
|
||||
ref="uninstallDropzoneEl"
|
||||
class="flex-1 h-20 flex items-center justify-center gap-2 rounded-lg border-2 border-dashed transition-all"
|
||||
:class="
|
||||
isOverUninstallZone
|
||||
? 'bg-red-500/20 border-red-500 dark:bg-red-400/20 dark:border-red-400'
|
||||
: 'border-red-500/50 dark:border-red-400/50'
|
||||
"
|
||||
>
|
||||
<Icon
|
||||
name="i-heroicons-trash"
|
||||
class="w-6 h-6"
|
||||
:class="
|
||||
isOverUninstallZone
|
||||
? 'text-red-700 dark:text-red-300'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
"
|
||||
/>
|
||||
<span
|
||||
class="font-semibold"
|
||||
:class="
|
||||
isOverUninstallZone
|
||||
? 'text-red-700 dark:text-red-300'
|
||||
: 'text-red-600 dark:text-red-400'
|
||||
"
|
||||
>
|
||||
Deinstallieren
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<HaexDesktopIcon
|
||||
v-for="item in desktopItemIcons"
|
||||
:key="item.id"
|
||||
:id="item.id"
|
||||
:item-type="item.itemType"
|
||||
:reference-id="item.referenceId"
|
||||
:initial-x="item.positionX"
|
||||
:initial-y="item.positionY"
|
||||
:label="item.label"
|
||||
:icon="item.icon"
|
||||
@position-changed="handlePositionChanged"
|
||||
@open="handleOpen"
|
||||
@drag-start="handleDragStart"
|
||||
@drag-end="handleDragEnd"
|
||||
@uninstall="handleUninstall"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const desktopStore = useDesktopStore()
|
||||
const extensionsStore = useExtensionsStore()
|
||||
const router = useRouter()
|
||||
|
||||
const { desktopItems } = storeToRefs(desktopStore)
|
||||
const { availableExtensions } = storeToRefs(extensionsStore)
|
||||
|
||||
// Drag state
|
||||
const isDragging = ref(false)
|
||||
const currentDraggedItemId = ref<string>()
|
||||
const currentDraggedItemType = ref<string>()
|
||||
const currentDraggedReferenceId = ref<string>()
|
||||
|
||||
// Dropzone refs
|
||||
const removeDropzoneEl = ref<HTMLElement>()
|
||||
const uninstallDropzoneEl = ref<HTMLElement>()
|
||||
|
||||
// Setup dropzones with VueUse
|
||||
const { isOverDropZone: isOverRemoveZone } = useDropZone(removeDropzoneEl, {
|
||||
onDrop: () => {
|
||||
if (currentDraggedItemId.value) {
|
||||
handleRemoveFromDesktop(currentDraggedItemId.value)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const { isOverDropZone: isOverUninstallZone } = useDropZone(uninstallDropzoneEl, {
|
||||
onDrop: () => {
|
||||
if (currentDraggedItemType.value && currentDraggedReferenceId.value) {
|
||||
handleUninstall(currentDraggedItemType.value, currentDraggedReferenceId.value)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
interface DesktopItemIcon extends IDesktopItem {
|
||||
label: string
|
||||
icon?: string
|
||||
}
|
||||
|
||||
const desktopItemIcons = computed<DesktopItemIcon[]>(() => {
|
||||
return desktopItems.value.map((item) => {
|
||||
if (item.itemType === 'extension') {
|
||||
const extension = availableExtensions.value.find(
|
||||
(ext) => ext.id === item.referenceId,
|
||||
)
|
||||
|
||||
return {
|
||||
...item,
|
||||
label: extension?.name || 'Unknown',
|
||||
icon: extension?.icon || '',
|
||||
}
|
||||
}
|
||||
|
||||
if (item.itemType === 'file') {
|
||||
// Für später: file handling
|
||||
return {
|
||||
...item,
|
||||
label: item.referenceId,
|
||||
icon: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
if (item.itemType === 'folder') {
|
||||
// Für später: folder handling
|
||||
return {
|
||||
...item,
|
||||
label: item.referenceId,
|
||||
icon: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
label: item.referenceId,
|
||||
icon: undefined,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const handlePositionChanged = async (id: string, x: number, y: number) => {
|
||||
try {
|
||||
await desktopStore.updateDesktopItemPositionAsync(id, x, y)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern der Position:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const handleOpen = (itemType: string, referenceId: string) => {
|
||||
if (itemType === 'extension') {
|
||||
router.push(
|
||||
localePath({
|
||||
name: 'extension',
|
||||
params: { extensionId: referenceId },
|
||||
})
|
||||
)
|
||||
}
|
||||
// Für später: file und folder handling
|
||||
}
|
||||
|
||||
const handleDragStart = (id: string, itemType: string, referenceId: string) => {
|
||||
isDragging.value = true
|
||||
currentDraggedItemId.value = id
|
||||
currentDraggedItemType.value = itemType
|
||||
currentDraggedReferenceId.value = referenceId
|
||||
}
|
||||
|
||||
const handleDragEnd = () => {
|
||||
isDragging.value = false
|
||||
currentDraggedItemId.value = undefined
|
||||
currentDraggedItemType.value = undefined
|
||||
currentDraggedReferenceId.value = undefined
|
||||
}
|
||||
|
||||
const handleUninstall = async (itemType: string, referenceId: string) => {
|
||||
if (itemType === 'extension') {
|
||||
try {
|
||||
const extension = availableExtensions.value.find((ext) => ext.id === referenceId)
|
||||
if (extension) {
|
||||
await extensionsStore.removeExtensionAsync(
|
||||
extension.publicKey,
|
||||
extension.name,
|
||||
extension.version,
|
||||
)
|
||||
// Reload extensions after uninstall
|
||||
await extensionsStore.loadExtensionsAsync()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Deinstallieren:', error)
|
||||
}
|
||||
}
|
||||
// Für später: file und folder handling
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await desktopStore.loadDesktopItemsAsync()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.slide-down-enter-active,
|
||||
.slide-down-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-down-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.slide-down-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
</style>
|
||||
@ -67,6 +67,12 @@
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<!-- Add to Desktop Option -->
|
||||
<UCheckbox
|
||||
v-model="addToDesktop"
|
||||
:label="t('addToDesktop')"
|
||||
/>
|
||||
|
||||
<!-- Permissions Section -->
|
||||
<div class="flex flex-col gap-4">
|
||||
<h4 class="text-lg font-semibold">
|
||||
@ -140,6 +146,7 @@ const open = defineModel<boolean>('open', { default: false })
|
||||
const preview = defineModel<ExtensionPreview | null>('preview', {
|
||||
default: null,
|
||||
})
|
||||
const addToDesktop = ref(true)
|
||||
|
||||
const databasePermissions = computed({
|
||||
get: () => preview.value?.editable_permissions?.database || [],
|
||||
@ -217,7 +224,10 @@ const permissionAccordionItems = computed(() => {
|
||||
return items
|
||||
})
|
||||
|
||||
const emit = defineEmits(['deny', 'confirm'])
|
||||
const emit = defineEmits<{
|
||||
deny: []
|
||||
confirm: [addToDesktop: boolean]
|
||||
}>()
|
||||
|
||||
const onDeny = () => {
|
||||
open.value = false
|
||||
@ -226,7 +236,7 @@ const onDeny = () => {
|
||||
|
||||
const onConfirm = () => {
|
||||
open.value = false
|
||||
emit('confirm')
|
||||
emit('confirm', addToDesktop.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -235,6 +245,7 @@ de:
|
||||
title: Erweiterung installieren
|
||||
version: Version
|
||||
author: Autor
|
||||
addToDesktop: Zum Desktop hinzufügen
|
||||
signature:
|
||||
valid: Signatur verifiziert
|
||||
invalid: Signatur ungültig
|
||||
@ -249,6 +260,7 @@ en:
|
||||
title: Install Extension
|
||||
version: Version
|
||||
author: Author
|
||||
addToDesktop: Add to Desktop
|
||||
signature:
|
||||
valid: Signature verified
|
||||
invalid: Invalid signature
|
||||
|
||||
@ -422,7 +422,7 @@ async function handleContextMethodAsync(request: ExtensionRequest) {
|
||||
return {
|
||||
theme: contextGetters.getTheme(),
|
||||
locale: contextGetters.getLocale(),
|
||||
platform: detectPlatform(),
|
||||
platform: contextGetters.getPlatform(),
|
||||
}
|
||||
|
||||
default:
|
||||
@ -430,13 +430,6 @@ async function handleContextMethodAsync(request: ExtensionRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
function detectPlatform(): 'desktop' | 'mobile' | 'tablet' {
|
||||
const width = window.innerWidth
|
||||
if (width < 768) return 'mobile'
|
||||
if (width < 1024) return 'tablet'
|
||||
return 'desktop'
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Storage Methods
|
||||
// ==========================================
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="">
|
||||
<!-- class="" -->
|
||||
<div class="flex flex-col w-full h-full overflow-hidden">
|
||||
<UPageHeader
|
||||
as="header"
|
||||
:ui="{
|
||||
@ -33,7 +32,7 @@
|
||||
</template>
|
||||
</UPageHeader>
|
||||
|
||||
<main class="overflow-scroll flex bg-elevated">
|
||||
<main class="flex-1 overflow-hidden bg-elevated">
|
||||
<NuxtPage />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="bg-default isolate h-dvh">
|
||||
<div class="bg-default isolate w-dvw h-dvh flex flex-col">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -100,7 +100,11 @@ const { syncLastVaultsAsync, removeVaultAsync } = useLastVaultStore()
|
||||
const { lastVaults } = storeToRefs(useLastVaultStore())
|
||||
|
||||
onMounted(async () => {
|
||||
await syncLastVaultsAsync()
|
||||
try {
|
||||
await syncLastVaultsAsync()
|
||||
} catch (error) {
|
||||
console.error('ERROR: ', error)
|
||||
}
|
||||
})
|
||||
|
||||
const onSelectLocale = async (locale: Locale) => {
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
:ui="{
|
||||
root: ['h-full w-full bg-elevated lg:flex'],
|
||||
center: ['h-full w-full'],
|
||||
}"
|
||||
>
|
||||
<div class="w-full h-full overflow-y-auto">
|
||||
<NuxtLayout name="app">
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
@ -57,14 +52,18 @@ const { setDeviceIdIfNotExistsAsync, addDeviceNameAsync } = useDeviceStore()
|
||||
const { deviceId } = storeToRefs(useDeviceStore())
|
||||
|
||||
onMounted(async () => {
|
||||
await setDeviceIdIfNotExistsAsync()
|
||||
await loadExtensionsAsync()
|
||||
await readNotificationsAsync()
|
||||
try {
|
||||
await setDeviceIdIfNotExistsAsync()
|
||||
await loadExtensionsAsync()
|
||||
await readNotificationsAsync()
|
||||
|
||||
if (!(await isKnownDeviceAsync())) {
|
||||
console.log('not known device')
|
||||
newDeviceName.value = hostname.value ?? 'unknown'
|
||||
showNewDeviceDialog.value = true
|
||||
if (!(await isKnownDeviceAsync())) {
|
||||
console.log('not known device')
|
||||
newDeviceName.value = hostname.value ?? 'unknown'
|
||||
showNewDeviceDialog.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('vault mount error:', error)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -206,7 +206,7 @@ import {
|
||||
} from '~/config/constants'
|
||||
|
||||
definePageMeta({
|
||||
name: 'haexExtension',
|
||||
name: 'extension',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -104,7 +104,7 @@
|
||||
<HaexExtensionDialogInstall
|
||||
v-model:open="showConfirmation"
|
||||
:preview="preview"
|
||||
@confirm="addExtensionAsync"
|
||||
@confirm="(addToDesktop) => addExtensionAsync(addToDesktop)"
|
||||
/>
|
||||
|
||||
<HaexExtensionDialogRemove
|
||||
@ -130,6 +130,7 @@ definePageMeta({
|
||||
|
||||
const { t } = useI18n()
|
||||
const extensionStore = useExtensionsStore()
|
||||
const desktopStore = useDesktopStore()
|
||||
|
||||
const showConfirmation = ref(false)
|
||||
const openOverwriteDialog = ref(false)
|
||||
@ -388,18 +389,23 @@ const onSelectExtensionAsync = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const addExtensionAsync = async () => {
|
||||
const addExtensionAsync = async (addToDesktop: boolean = false) => {
|
||||
try {
|
||||
console.log(
|
||||
'preview.value?.editable_permissions',
|
||||
preview.value?.editable_permissions,
|
||||
)
|
||||
await extensionStore.installAsync(
|
||||
const extensionId = await extensionStore.installAsync(
|
||||
extension.path,
|
||||
preview.value?.editable_permissions,
|
||||
)
|
||||
await extensionStore.loadExtensionsAsync()
|
||||
|
||||
// Add to desktop if requested
|
||||
if (addToDesktop && extensionId) {
|
||||
await desktopStore.addDesktopItemAsync('extension', extensionId, 50, 50)
|
||||
}
|
||||
|
||||
add({
|
||||
color: 'success',
|
||||
title: t('extension.success.title', {
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="h-screen bg-amber-300 flex-1 flex-wrap">
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
</div>
|
||||
<div class="h-screen bg-teal-300 flex-1">
|
||||
abbbbbbbbbbbbbbbbbbbbb availableThemes:{{ uiStore.availableThemes }}
|
||||
</div>
|
||||
<div class="w-full h-full flex items-center justify-center">
|
||||
<HaexDesktop />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -13,6 +8,4 @@
|
||||
definePageMeta({
|
||||
name: 'vaultOverview',
|
||||
})
|
||||
|
||||
const uiStore = useUiStore()
|
||||
</script>
|
||||
|
||||
@ -57,14 +57,14 @@ function interceptConsole(level: 'log' | 'info' | 'warn' | 'error' | 'debug') {
|
||||
}
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
// Install console interceptors immediately
|
||||
interceptConsole('log')
|
||||
interceptConsole('info')
|
||||
interceptConsole('warn')
|
||||
interceptConsole('error')
|
||||
interceptConsole('debug')
|
||||
// TEMPORARILY DISABLED - Console interceptor causes too many logs
|
||||
// interceptConsole('log')
|
||||
// interceptConsole('info')
|
||||
// interceptConsole('warn')
|
||||
// interceptConsole('error')
|
||||
// interceptConsole('debug')
|
||||
|
||||
console.log('[HaexHub] Global console interceptor installed')
|
||||
// console.log('[HaexHub] Global console interceptor installed')
|
||||
|
||||
return {
|
||||
provide: {
|
||||
|
||||
170
src/stores/vault/desktop.ts
Normal file
170
src/stores/vault/desktop.ts
Normal file
@ -0,0 +1,170 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { haexDesktopItems } from '~~/src-tauri/database/schemas'
|
||||
import type {
|
||||
InsertHaexDesktopItems,
|
||||
SelectHaexDesktopItems,
|
||||
} from '~~/src-tauri/database/schemas'
|
||||
|
||||
export type DesktopItemType = 'extension' | 'file' | 'folder'
|
||||
|
||||
export interface IDesktopItem extends SelectHaexDesktopItems {
|
||||
label?: string
|
||||
icon?: string
|
||||
}
|
||||
|
||||
export const useDesktopStore = defineStore('desktopStore', () => {
|
||||
const { currentVault } = storeToRefs(useVaultStore())
|
||||
|
||||
const desktopItems = ref<IDesktopItem[]>([])
|
||||
|
||||
const loadDesktopItemsAsync = async () => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
console.error('Kein Vault geöffnet')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const items = await currentVault.value.drizzle
|
||||
.select()
|
||||
.from(haexDesktopItems)
|
||||
|
||||
desktopItems.value = items
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Desktop-Items:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const addDesktopItemAsync = async (
|
||||
itemType: DesktopItemType,
|
||||
referenceId: string,
|
||||
positionX: number = 0,
|
||||
positionY: number = 0,
|
||||
) => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
|
||||
try {
|
||||
const newItem: InsertHaexDesktopItems = {
|
||||
itemType: itemType,
|
||||
referenceId: referenceId,
|
||||
positionX: positionX,
|
||||
positionY: positionY,
|
||||
}
|
||||
|
||||
const result = await currentVault.value.drizzle
|
||||
.insert(haexDesktopItems)
|
||||
.values(newItem)
|
||||
.returning()
|
||||
|
||||
if (result.length > 0 && result[0]) {
|
||||
desktopItems.value.push(result[0])
|
||||
return result[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hinzufügen des Desktop-Items:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const updateDesktopItemPositionAsync = async (
|
||||
id: string,
|
||||
positionX: number,
|
||||
positionY: number,
|
||||
) => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await currentVault.value.drizzle
|
||||
.update(haexDesktopItems)
|
||||
.set({
|
||||
positionX: positionX,
|
||||
positionY: positionY,
|
||||
})
|
||||
.where(eq(haexDesktopItems.id, id))
|
||||
.returning()
|
||||
|
||||
if (result.length > 0 && result[0]) {
|
||||
const index = desktopItems.value.findIndex((item) => item.id === id)
|
||||
if (index !== -1) {
|
||||
desktopItems.value[index] = result[0]
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren der Position:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const removeDesktopItemAsync = async (id: string) => {
|
||||
if (!currentVault.value?.drizzle) {
|
||||
throw new Error('Kein Vault geöffnet')
|
||||
}
|
||||
|
||||
try {
|
||||
// Soft delete using haexTombstone
|
||||
await currentVault.value.drizzle
|
||||
.delete(haexDesktopItems)
|
||||
.where(eq(haexDesktopItems.id, id))
|
||||
|
||||
desktopItems.value = desktopItems.value.filter((item) => item.id !== id)
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Entfernen des Desktop-Items:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getDesktopItemByReference = (
|
||||
itemType: DesktopItemType,
|
||||
referenceId: string,
|
||||
) => {
|
||||
return desktopItems.value.find(
|
||||
(item) => item.itemType === itemType && item.referenceId === referenceId,
|
||||
)
|
||||
}
|
||||
|
||||
const getContextMenuItems = (
|
||||
id: string,
|
||||
itemType: DesktopItemType,
|
||||
referenceId: string,
|
||||
onOpen: () => void,
|
||||
onUninstall: () => void,
|
||||
) => {
|
||||
return [
|
||||
[
|
||||
{
|
||||
label: 'Öffnen',
|
||||
icon: 'i-heroicons-arrow-top-right-on-square',
|
||||
click: onOpen,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
label: 'Von Desktop entfernen',
|
||||
icon: 'i-heroicons-x-mark',
|
||||
click: async () => {
|
||||
await removeDesktopItemAsync(id)
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Deinstallieren',
|
||||
icon: 'i-heroicons-trash',
|
||||
click: onUninstall,
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
desktopItems,
|
||||
loadDesktopItemsAsync,
|
||||
addDesktopItemAsync,
|
||||
updateDesktopItemPositionAsync,
|
||||
removeDesktopItemAsync,
|
||||
getDesktopItemByReference,
|
||||
getContextMenuItems,
|
||||
}
|
||||
})
|
||||
@ -51,11 +51,12 @@ export const useDeviceStore = defineStore('vaultInstanceStore', () => {
|
||||
|
||||
const readDeviceNameAsync = async (id?: string) => {
|
||||
const { readDeviceNameAsync } = useVaultSettingsStore()
|
||||
const _id = id ?? deviceId.value
|
||||
const _id = id || deviceId.value
|
||||
console.log('readDeviceNameAsync id', _id)
|
||||
if (!_id) return
|
||||
|
||||
deviceName.value = (await readDeviceNameAsync(_id))?.value ?? ''
|
||||
|
||||
return deviceName.value
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
import { drizzle } from 'drizzle-orm/sqlite-proxy'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { schema } from '@/../src-tauri/database/index'
|
||||
import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy'
|
||||
import type {
|
||||
AsyncRemoteCallback,
|
||||
SqliteRemoteDatabase,
|
||||
} from 'drizzle-orm/sqlite-proxy'
|
||||
|
||||
interface IVault {
|
||||
name: string
|
||||
@ -55,40 +58,10 @@ export const useVaultStore = defineStore('vaultStore', () => {
|
||||
...openVaults.value,
|
||||
[vaultId]: {
|
||||
name: fileName,
|
||||
drizzle: drizzle<typeof schema>(
|
||||
async (sql, params: unknown[], method) => {
|
||||
let rows: any[] = []
|
||||
let results: any[] = []
|
||||
|
||||
// If the query is a SELECT, use the select method
|
||||
if (isSelectQuery(sql)) {
|
||||
console.log('sql_select', sql, params, method)
|
||||
rows = await invoke<unknown[]>('sql_select', {
|
||||
sql,
|
||||
params,
|
||||
}).catch((e) => {
|
||||
console.error('SQL select Error:', e, sql, params)
|
||||
return []
|
||||
})
|
||||
} else {
|
||||
console.log('sql_execute', sql, params, method)
|
||||
// Otherwise, use the execute method
|
||||
rows = await invoke<unknown[]>('sql_execute', {
|
||||
sql,
|
||||
params,
|
||||
}).catch((e) => {
|
||||
console.error('SQL execute Error:', e, sql, params)
|
||||
return []
|
||||
})
|
||||
return { rows: [] }
|
||||
}
|
||||
|
||||
results = method === 'all' ? rows : rows[0]
|
||||
|
||||
return { rows: results }
|
||||
},
|
||||
{ schema: schema, logger: true },
|
||||
),
|
||||
drizzle: drizzle<typeof schema>(drizzleCallback, {
|
||||
schema: schema,
|
||||
logger: false,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@ -141,7 +114,34 @@ const getVaultIdAsync = async (path: string) => {
|
||||
}
|
||||
|
||||
const isSelectQuery = (sql: string) => {
|
||||
console.log('check isSelectQuery', sql)
|
||||
const selectRegex = /^\s*SELECT\b/i
|
||||
return selectRegex.test(sql)
|
||||
}
|
||||
|
||||
const drizzleCallback = (async (
|
||||
sql: string,
|
||||
params: unknown[],
|
||||
method: 'get' | 'run' | 'all' | 'values',
|
||||
) => {
|
||||
let rows: unknown[] = []
|
||||
|
||||
if (isSelectQuery(sql)) {
|
||||
rows = await invoke<unknown[]>('sql_select', { sql, params }).catch((e) => {
|
||||
console.error('SQL select Error:', e, sql, params)
|
||||
return []
|
||||
})
|
||||
} else {
|
||||
rows = await invoke<unknown[]>('sql_execute', { sql, params }).catch(
|
||||
(e) => {
|
||||
console.error('SQL execute Error:', e, sql, params)
|
||||
return []
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (method === 'get') {
|
||||
return { rows: rows.length > 0 ? [rows[0]] : [] }
|
||||
} else {
|
||||
return { rows }
|
||||
}
|
||||
}) satisfies AsyncRemoteCallback
|
||||
|
||||
@ -128,7 +128,7 @@ export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
|
||||
eq(schema.haexSettings.key, id),
|
||||
),
|
||||
})
|
||||
console.log('readDeviceNameAsync', deviceName)
|
||||
console.log('store: readDeviceNameAsync', deviceName)
|
||||
return deviceName
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user