Compare commits

...

2 Commits

3 changed files with 112 additions and 4 deletions
+56
View File
@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prism Notes</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='75' font-size='75'>📝</text></svg>">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
@@ -2794,6 +2795,61 @@
}
const noteRecord = await pb.collection(NOTES_COLLECTION).create(payload);
// TEST: Play alert sound only if current user is in shared_with
if (selectedUsers.length > 0) {
try {
console.log(`🔔 Checking if current user (${userEmailCache}) is in shared_with...`);
let currentUserIsRecipient = false;
for (const user of selectedUsers) {
const firstName = user.name.split(' ')[0];
const records = await pb.collection('Associations').getList(1, 50, {
filter: `first_name = "${firstName}"`,
});
if (records.items.length > 0) {
const match = records.items.find(r => r.emailtext === userEmailCache);
if (match) {
currentUserIsRecipient = true;
console.log(`✓ Found match: ${user.name} (${match.emailtext})`);
break;
}
}
}
if (currentUserIsRecipient) {
console.log('🔔 Current user is a recipient - playing alert sound');
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
if (audioContext.state === 'suspended') {
await audioContext.resume();
}
const now = audioContext.currentTime;
const playBell = (startTime, volume = 1) => {
const osc = audioContext.createOscillator();
const gain = audioContext.createGain();
osc.connect(gain);
gain.connect(audioContext.destination);
osc.frequency.setValueAtTime(1000, startTime);
osc.frequency.exponentialRampToValueAtTime(600, startTime + 0.15);
gain.gain.setValueAtTime(0.8 * volume, startTime);
gain.gain.exponentialRampToValueAtTime(0.01, startTime + 0.2);
osc.start(startTime);
osc.stop(startTime + 0.2);
};
playBell(now, 1);
playBell(now + 0.2, 0.7);
console.log('✓ Alert sound played');
} else {
console.log(' Current user is the creator - no alert sound');
}
} catch (err) {
console.error('Error playing alert sound:', err);
}
}
const attachmentRecords = [];
let audioAttachmentCount = 0;
let fileAttachmentCount = 0;
+36
View File
@@ -167,4 +167,40 @@ Project history, decisions, and architectural milestones
becomes more established and needs full file separation
- Status: Functional and ready for independent container updates
[2026-01-15 INVESTIGATION] PocketBase Associations Collection API Rules
- Problem: User email lookup queries Associations → "only superusers can perform this action"
- Root Cause: Associations collection LIST API rule is restricted (default deny all)
- Solution: Set Associations LIST API rule to `@request.auth.id != ""` (allow authenticated users)
- Forum Source: https://github.com/pocketbase/pocketbase/discussions/5948#5948-answer
* Official pattern for "authenticate first" checks
* Works for all read/write operations
* Syntax: `@request.auth.id != ""` (empty string = anonymous/unauthenticated)
- Implementation (Manual in PocketBase Admin):
1. Go to PocketBase Admin Console
2. Select Associations collection
3. API Rules tab → List action
4. Set rule to: `@request.auth.id != ""`
5. Save and test with authenticated user
- Why UserAlertSystem Works: Unknown at this point (may have different API rule set)
- Status: Solution identified, requires manual PocketBase admin console update
[2026-01-16 FEATURE] Note Creation Alerts - Notify Shared Users
- Problem: When a note is created and shared with users, those users should receive alerts
- Solution: Use existing UserAlertSystem realtime monitoring
- How it works:
* When note is created, it's published with `shared: true` and `shared_with: [names]`
* UserAlertSystem monitors Notes collection in realtime via PocketBase
* When new note event arrives, system checks if current user is in shared_with
* If match, plays notification sound automatically
- No code changes needed to submitNote - just ensure:
* Note is created with `shared: true` and `shared_with` populated (already done)
* UserAlertSystem is running in user's browser (via index.html)
* Realtime subscription is active (UserAlertSystem handles this)
- Benefits:
* No database records needed for alerts
* Real-time notification (minimal latency)
* Works for self-sharing (user shares note with self)
- Status: Simplified approach - removed unnecessary Alerts collection code
================================================================================
+20 -4
View File
@@ -38,21 +38,36 @@ async function getEmailByFirstName(pb, firstName) {
}
try {
// First, get ALL records to inspect structure
const allRecords = await pb.collection('Associations').getList(1, 50);
console.log(`[Email Lookup] All Associations records:`, allRecords.items);
if (allRecords.items.length > 0) {
console.log(`[Email Lookup] Sample record fields:`, Object.keys(allRecords.items[0]));
console.log(`[Email Lookup] Sample record:`, allRecords.items[0]);
}
// Now try the filtered query
const records = await pb.collection('Associations').getList(1, 50, {
filter: `first_name = "${firstName}"`,
});
console.log(`[Email Lookup] Query: first_name="${firstName}" | Filtered results:`, records.items);
if (records.items.length === 0) {
console.log(`[Email Lookup] No records found for: ${firstName}`);
emailLookupCache[firstName] = 'Email not found';
return 'Email not found';
}
// Use first match's email
const email = records.items[0].email || 'Email not found';
// Use first match's emailtext field
const firstRecord = records.items[0];
console.log(`[Email Lookup] First record:`, firstRecord);
const email = firstRecord.emailtext || 'Email not found';
emailLookupCache[firstName] = email;
return email;
} catch (error) {
console.error(`Failed to lookup email for "${firstName}":`, error);
console.error(`Error response:`, error.response?.data);
return 'Error loading email';
}
}
@@ -74,8 +89,6 @@ window.setupUserEmailLookup = function(pb, containerSelector) {
}
console.log(`✓ User Email Lookup initialized for: ${containerSelector}`);
// Observer: Watch for new capsule additions
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
@@ -115,11 +128,14 @@ function attachEmailLookupTooltip(pb, capsuleEl) {
const firstName = nameSpan.textContent.trim();
if (!firstName) return;
console.log(`[Email Lookup] Attached to user: ${firstName}`);
// Mark as processed
nameSpan.setAttribute('data-email-loaded', 'true');
// Attach hover listener
capsuleEl.addEventListener('mouseenter', async () => {
console.log(`[Email Lookup] Hover on: ${firstName}`);
// If email already in title, don't query again
if (capsuleEl.title && !capsuleEl.title.startsWith('Loading')) {
return;