All files / src/components/list LinkDevicesModal.vue

90.9% Statements 30/33
86.36% Branches 19/22
83.33% Functions 5/6
100% Lines 28/28

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>