Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 9x 9x 9x 9x 9x 9x 9x 9x 16x 7x 7x 7x 7x 7x 7x 6x 6x 6x 2x 2x 2x 2x 16x 1x 1x 1x 1x 24x | <script setup lang="ts">
import { ref, watch } from 'vue'
import { shareService } from '../../services/share'
import { useThemeStore } from '../../stores/theme'
const props = defineProps<{ modelValue: boolean }>()
const emit = defineEmits<{ 'update:modelValue': [val: boolean] }>()
const close = () => emit('update:modelValue', false)
const themeStore = useThemeStore()
const loading = ref(false)
const copied = ref(false)
const syncUrl = ref<string | null>(null)
const expiresAt = ref<string | null>(null)
watch(() => props.modelValue, async (open) => {
Iif (!open) return
copied.value = false
syncUrl.value = null
loading.value = true
try {
const res = await shareService.createSyncToken(themeStore.theme)
syncUrl.value = `${window.location.origin}/sync/${res.token}`
expiresAt.value = new Date(res.expiresAt).toLocaleDateString('de-DE', {
day: '2-digit', month: '2-digit', year: 'numeric',
})
} finally {
loading.value = false
}
})
async function copyLink() {
Iif (!syncUrl.value) return
await navigator.clipboard.writeText(syncUrl.value)
copied.value = true
setTimeout(() => { copied.value = false }, 2000)
}
</script>
<template>
<Transition name="sheet">
<div v-if="modelValue" class="fixed inset-0 z-50 flex flex-col justify-end">
<!-- Backdrop -->
<div class="absolute inset-0 bg-ctp-base/70 backdrop-blur-sm" @click="close" />
<!-- Panel -->
<div class="relative bg-ctp-mantle border-t border-ctp-surface0 rounded-t-3xl px-5 pt-4 pb-10 safe-bottom">
<!-- Handle -->
<div class="w-10 h-1 bg-ctp-surface1 rounded-full mx-auto mb-5" />
<h2 class="text-lg font-bold text-ctp-text mb-1">Geräte verknüpfen</h2>
<p class="text-sm text-ctp-subtext0 mb-5">
Öffne diesen Link auf einem anderen Gerät, um alle deine Listen zu übertragen.
</p>
<!-- Loading -->
<div v-if="loading" class="space-y-3">
<div class="h-12 bg-ctp-surface0 rounded-xl skeleton" />
<div class="h-12 bg-ctp-surface0 rounded-xl skeleton" />
</div>
<!-- Sync URL -->
<div v-else-if="syncUrl" class="space-y-3">
<div class="flex items-center gap-2 bg-ctp-surface0 rounded-xl px-4 py-3">
<span class="flex-1 text-sm text-ctp-text truncate font-mono">{{ syncUrl }}</span>
</div>
<button
@click="copyLink"
class="w-full py-3 rounded-xl font-semibold text-sm transition-colors"
:class="copied
? 'bg-ctp-green/20 text-ctp-green'
: 'bg-ctp-teal text-ctp-base active:scale-95'"
>
{{ copied ? '✓ Kopiert!' : 'Link kopieren' }}
</button>
<p v-if="expiresAt" class="text-xs text-ctp-overlay0 text-center">
Link gültig bis {{ expiresAt }}
</p>
</div>
<button
@click="close"
class="w-full mt-3 py-2.5 text-ctp-subtext0 text-sm rounded-xl hover:text-ctp-text transition-colors"
>
Schließen
</button>
</div>
</div>
</Transition>
</template>
<style scoped>
.sheet-enter-active,
.sheet-leave-active {
transition: all 0.3s ease;
}
.sheet-enter-from,
.sheet-leave-to {
opacity: 0;
}
</style>
|