feat(share): after sharing, send the entry on as task or list item with one tap
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m30s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-31 11:27:06 +01:00
1 parent 3d367ce633
commit da258fdce2
7 files changed
+200 -9

No files matched your search

@@ -0,0 +1,100 @@
import React, { useEffect, useState } from 'react';
import { ArrowRight, CheckSquare, Inbox, ShoppingCart, X } from 'lucide-react';
import { api, triggerHaptic } from '../lib/api';
import type { SharedList } from '../lib/types';
// After sharing from another app: the items are already in the Quick Dump (nothing to tap), and for a
// few seconds they can be sent on as a task or onto a list instead – one tap, the flow stays as fast.
const AUTO_HIDE_MS = 8000;
interface SharedImportToastProps {
thoughtIds: string[]; // created on the server (offline captures have no id yet and get no buttons)
lists: SharedList[];
onClose: () => void;
onConverted: (message: string) => void;
}
export const SharedImportToast: React.FC<SharedImportToastProps> = ({ thoughtIds, lists, onClose, onConverted }) => {
const [choosingList, setChoosingList] = useState(false);
const [busy, setBusy] = useState(false);
const [failed, setFailed] = useState(false);
const writableLists = lists.filter((l) => l.isOwner || l.role !== 'viewer');
const count = thoughtIds.length;
// Disappears on its own unless a list is being picked or a conversion runs
useEffect(() => {
if (choosingList || busy) return;
const timer = window.setTimeout(onClose, AUTO_HIDE_MS);
return () => window.clearTimeout(timer);
}, [choosingList, busy, onClose]);
const convert = async (convertTo: 'task' | 'list_item', list?: SharedList) => {
triggerHaptic(15);
setBusy(true);
setFailed(false);
try {
for (const id of thoughtIds) {
await api.triageAction(id, 'keep', { convertTo, targetListId: list?.id });
}
const what = count === 1 ? 'Eintrag' : `${count} Einträge`;
onConverted(convertTo === 'task' ? `${count === 1 ? 'Als Aufgabe' : `${count} Aufgaben`} angelegt` : `${what} auf „${list?.title}“`);
} catch {
setFailed(true);
setBusy(false);
}
};
const toList = () => {
if (writableLists.length === 1) convert('list_item', writableLists[0]);
else setChoosingList(true);
};
const chip =
'h-9 px-3 inline-flex items-center gap-1.5 rounded-btn border border-border bg-canvas text-main text-xs font-medium active:bg-panel-elevated disabled:opacity-50 touch-manipulation';
return (
<div
role="status"
className="fixed left-1/2 -translate-x-1/2 bottom-[calc(5.5rem+env(safe-area-inset-bottom))] sm:bottom-4 z-[60] w-[calc(100vw-1.5rem)] max-w-sm panel-surface shadow-panel overflow-hidden p-3 space-y-2.5 animate-in fade-in slide-in-from-bottom-2 duration-200"
>
<div className="flex items-center gap-2 text-sm text-main">
<Inbox className="w-4 h-4 text-accent shrink-0" />
<span className="flex-1 font-medium">
{count === 1 ? 'Im Quick Dump abgelegt' : `${count} Einträge im Quick Dump abgelegt`}
</span>
<button type="button" onClick={onClose} aria-label="Schließen" className="h-7 w-7 -mr-1 flex items-center justify-center text-faint">
<X className="w-4 h-4" />
</button>
</div>
{failed && <p className="text-xs text-status-err">Umwandeln hat nicht geklappt – der Eintrag bleibt im Quick Dump.</p>}
{!choosingList ? (
<div className="flex items-center gap-2">
<span className="text-xs text-muted">Stattdessen</span>
<button type="button" className={chip} disabled={busy} onClick={() => convert('task')}>
<ArrowRight className="w-3.5 h-3.5 text-faint" />
<CheckSquare className="w-3.5 h-3.5 text-accent" />
Aufgabe
</button>
{writableLists.length > 0 && (
<button type="button" className={chip} disabled={busy} onClick={toList}>
<ArrowRight className="w-3.5 h-3.5 text-faint" />
<ShoppingCart className="w-3.5 h-3.5 text-accent" />
Liste
</button>
)}
</div>
) : (
<div className="flex flex-wrap items-center gap-2">
{writableLists.map((l) => (
<button key={l.id} type="button" className={chip} disabled={busy} onClick={() => convert('list_item', l)}>
<span>{l.icon}</span>
{l.title}
</button>
))}
</div>
)}
</div>
);
};