The app becomes a Rails 8 application; the Vue frontend moves to app/frontend and is served through vite_rails. - Without an account documents stay in the browser (IndexedDB). Sign-in is by magic link or the 6-digit code from the same mail; afterwards local documents can be uploaded into the account. - Server documents (/d/<id>) with versioned saves and a single-editor lock per browser tab; others see them read-only and follow by polling. - Sharing via link (works without an account) and via e-mail invitation, roles viewer/editor; the owner manages links and people. - Store gains a read-only mode that blocks all content changes. - Solid Queue, Cache and Cable share the one PostgreSQL database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
94 lines
2.3 KiB
Vue
94 lines
2.3 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* Shown once after signing in when this browser still has local documents:
|
||
* which of them should move into the account (server)? Uploaded ones are
|
||
* removed from the browser. "Später" leaves everything as is -- uploading
|
||
* stays possible from the document list.
|
||
*/
|
||
import { ref, watch } from 'vue';
|
||
import { useLibrary } from '../store/library';
|
||
import BaseModal from './BaseModal.vue';
|
||
|
||
const props = defineProps<{ open: boolean }>();
|
||
const emit = defineEmits<{ (e: 'close'): void }>();
|
||
|
||
const library = useLibrary();
|
||
const selected = ref<Set<string>>(new Set());
|
||
const busy = ref(false);
|
||
|
||
watch(
|
||
() => props.open,
|
||
(o) => {
|
||
if (o) selected.value = new Set(library.state.docs.map((d) => d.id));
|
||
},
|
||
);
|
||
|
||
function toggle(id: string) {
|
||
const s = new Set(selected.value);
|
||
if (s.has(id)) s.delete(id);
|
||
else s.add(id);
|
||
selected.value = s;
|
||
}
|
||
|
||
async function run() {
|
||
busy.value = true;
|
||
try {
|
||
await library.upload([...selected.value]);
|
||
emit('close');
|
||
} finally {
|
||
busy.value = false;
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<BaseModal :open="open" title="Dokumente in dein Konto übernehmen?" @close="emit('close')">
|
||
<p class="lead">
|
||
In diesem Browser liegen noch Dokumente. Ausgewählte werden in dein Konto hochgeladen – dann sind sie
|
||
auf allen Geräten da und lassen sich teilen – und hier aus dem Browser entfernt.
|
||
</p>
|
||
<ul class="list">
|
||
<li v-for="d in library.state.docs" :key="d.id">
|
||
<label>
|
||
<input type="checkbox" :checked="selected.has(d.id)" @change="toggle(d.id)" />
|
||
{{ d.title }}
|
||
</label>
|
||
</li>
|
||
</ul>
|
||
<template #footer>
|
||
<button @click="emit('close')">Später</button>
|
||
<button class="primary" :disabled="busy || !selected.size" @click="run">
|
||
{{ busy ? 'Lade hoch …' : `${selected.size} hochladen` }}
|
||
</button>
|
||
</template>
|
||
</BaseModal>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.lead {
|
||
margin: 0 0 12px;
|
||
font-size: 13px;
|
||
line-height: 1.5;
|
||
}
|
||
.list {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
max-height: 40vh;
|
||
overflow-y: auto;
|
||
}
|
||
.list li + li {
|
||
margin-top: 4px;
|
||
}
|
||
.list label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 6px 8px;
|
||
border-radius: 6px;
|
||
background: var(--surface-2);
|
||
font-size: 13px;
|
||
cursor: pointer;
|
||
}
|
||
</style>
|