+ Remove {selectedMember.name} from this household?
+
+
+
+
+
+ Important: About Encrypted Data
+
+
+ This household uses end-to-end encryption. While {selectedMember.name} will
+ lose access to future updates, they may retain copies of any data they previously viewed
+ or downloaded. This is a limitation of client-side encryption - once data has been
+ decrypted on their device, we cannot remotely delete it.
+
+
+ If sensitive information was shared, consider creating a new household with fresh
+ encryption keys for maximum security.
+
+
+
+
+
+
+
+
+
+ )}
+
{/* Click outside to close menus */}
{showOptionsMenu && (
setShowOptionsMenu(false)} />
diff --git a/frontend/src/pages/ListPage.module.css b/frontend/src/pages/ListPage.module.css
index 3ddd4da..dc3c230 100644
--- a/frontend/src/pages/ListPage.module.css
+++ b/frontend/src/pages/ListPage.module.css
@@ -168,6 +168,12 @@
border-color: var(--color-warning);
}
+.actionButton.encryptedActive {
+ color: var(--color-success, #10b981);
+ background: rgba(16, 185, 129, 0.1);
+ border-color: var(--color-success, #10b981);
+}
+
/* Content Area */
.content {
flex: 1;
diff --git a/frontend/src/pages/ListPage.tsx b/frontend/src/pages/ListPage.tsx
index bd8ea6e..4655a07 100644
--- a/frontend/src/pages/ListPage.tsx
+++ b/frontend/src/pages/ListPage.tsx
@@ -1,6 +1,11 @@
import { useEffect, useState } from 'react';
import { useParams, useSearchParams, useNavigate } from 'react-router-dom';
-import { ArrowLeft, Trash2, FileText, CheckSquare, ShoppingCart, Pencil } from 'lucide-react';
+import { ArrowLeft, Trash2, FileText, CheckSquare, ShoppingCart, Pencil, Lock, Unlock } from 'lucide-react';
+import { isNoteEncrypted } from '../utils/encryptionHelpers';
+import { useCryptoStore } from '../stores/cryptoStore';
+import { useEncryption } from '../hooks/useEncryption';
+import { useUnlockModal } from '../hooks/useUnlockModal';
+import EncryptionSetup from '../components/EncryptionSetup';
import { useListsStore, List } from '../stores/store';
import { api } from '../utils/api';
import { encryptedApi } from '../utils/encryptedApi';
@@ -19,6 +24,13 @@ export default function ListPage() {
const [loading, setLoading] = useState(true);
const [editingTitle, setEditingTitle] = useState(false);
const [titleValue, setTitleValue] = useState('');
+ const [encrypting, setEncrypting] = useState(false);
+ const [showEncryptionSetup, setShowEncryptionSetup] = useState(false);
+ const { isUnlocked, hasEncryptionSetup } = useCryptoStore();
+ const { keyData } = useEncryption();
+ const openUnlockModal = useUnlockModal((s) => s.open);
+
+ const noteIsEncrypted = currentList ? isNoteEncrypted(currentList) : false;
useEffect(() => {
const loadList = async () => {
@@ -136,6 +148,51 @@ export default function ListPage() {
}
};
+ const handleToggleEncryption = async () => {
+ if (!currentList || !householdId || !listId) return;
+
+ // If encryption not set up, show setup
+ if (!hasEncryptionSetup) {
+ setShowEncryptionSetup(true);
+ return;
+ }
+
+ // If not unlocked, request unlock first
+ if (!isUnlocked) {
+ openUnlockModal(() => {
+ // After unlock, user can click again
+ });
+ return;
+ }
+
+ if (noteIsEncrypted) {
+ // Already encrypted — for now just inform
+ // TODO: implement decrypt (remove encryption from note)
+ alert('This note is encrypted. Decryption of individual notes is coming soon.');
+ return;
+ }
+
+ // Encrypt the note: re-save it through encryptedApi which will encrypt
+ setEncrypting(true);
+ try {
+ const updated = await encryptedApi.updateList(listId, householdId, {
+ title: currentList.title,
+ content: currentList.content,
+ items: currentList.items,
+ });
+ setCurrentList(updated);
+ updateList(listId, householdId, updated);
+ // Reload to get the encrypted version from server
+ const reloaded = await encryptedApi.getList(listId, householdId);
+ setCurrentList(reloaded);
+ } catch (error) {
+ console.error('Failed to encrypt note:', error);
+ alert('Failed to encrypt note. Please try again.');
+ } finally {
+ setEncrypting(false);
+ }
+ };
+
if (loading) {
return (
@@ -192,6 +249,14 @@ export default function ListPage() {