Add alert creation on note sharing - triggers alerts for shared_with users
This commit is contained in:
+21
@@ -2794,6 +2794,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const noteRecord = await pb.collection(NOTES_COLLECTION).create(payload);
|
const noteRecord = await pb.collection(NOTES_COLLECTION).create(payload);
|
||||||
|
|
||||||
|
// Create alerts for each shared_with user
|
||||||
|
if (selectedUsers.length > 0) {
|
||||||
|
console.log(`📢 Creating alerts for ${selectedUsers.length} users...`);
|
||||||
|
for (const user of selectedUsers) {
|
||||||
|
try {
|
||||||
|
const alertRecord = await pb.collection('Alerts').create({
|
||||||
|
note_id: noteRecord.id,
|
||||||
|
note_title: noteTitle || '(untitled)',
|
||||||
|
shared_name: user.name,
|
||||||
|
created_by: displayNameCache,
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
read: false,
|
||||||
|
});
|
||||||
|
console.log(`✓ Alert created for ${user.name}:`, alertRecord.id);
|
||||||
|
} catch (alertErr) {
|
||||||
|
console.error(`Error creating alert for ${user.name}:`, alertErr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const attachmentRecords = [];
|
const attachmentRecords = [];
|
||||||
let audioAttachmentCount = 0;
|
let audioAttachmentCount = 0;
|
||||||
let fileAttachmentCount = 0;
|
let fileAttachmentCount = 0;
|
||||||
|
|||||||
@@ -167,4 +167,44 @@ Project history, decisions, and architectural milestones
|
|||||||
becomes more established and needs full file separation
|
becomes more established and needs full file separation
|
||||||
- Status: Functional and ready for independent container updates
|
- 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 Implemented:
|
||||||
|
* When note is created with `shared_with` array
|
||||||
|
* Loop through each user in `shared_with`
|
||||||
|
* Create alert record in Alerts collection for each user
|
||||||
|
* Alert includes: note_id, note_title, shared_name, created_by, created_at, read flag
|
||||||
|
- Code Location: index.html ~line 2800 (in submitNote function, after noteRecord creation)
|
||||||
|
- Logic Flow:
|
||||||
|
1. Note created with `shared_with: selectedUsers.map(u => u.name)`
|
||||||
|
2. For each user in selectedUsers:
|
||||||
|
- Create Alerts record with note metadata
|
||||||
|
- Log alert creation for debugging
|
||||||
|
3. Alert system monitors Alerts collection for new records
|
||||||
|
4. Alert triggers notification/sound when created
|
||||||
|
- PocketBase Requirements:
|
||||||
|
* Alerts collection must exist with proper API rules
|
||||||
|
* Fields: note_id (relation to Notes), note_title (text), shared_name (text),
|
||||||
|
created_by (text), created_at (datetime), read (boolean)
|
||||||
|
* LIST API rule should allow authenticated users to read
|
||||||
|
- Status: Implementation complete in index.html, ready to test once Alerts collection is verified
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
|
|||||||
@@ -38,21 +38,36 @@ async function getEmailByFirstName(pb, firstName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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, {
|
const records = await pb.collection('Associations').getList(1, 50, {
|
||||||
filter: `first_name = "${firstName}"`,
|
filter: `first_name = "${firstName}"`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(`[Email Lookup] Query: first_name="${firstName}" | Filtered results:`, records.items);
|
||||||
|
|
||||||
if (records.items.length === 0) {
|
if (records.items.length === 0) {
|
||||||
|
console.log(`[Email Lookup] No records found for: ${firstName}`);
|
||||||
emailLookupCache[firstName] = 'Email not found';
|
emailLookupCache[firstName] = 'Email not found';
|
||||||
return 'Email not found';
|
return 'Email not found';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use first match's email
|
// Use first match's emailtext field
|
||||||
const email = records.items[0].email || 'Email not found';
|
const firstRecord = records.items[0];
|
||||||
|
console.log(`[Email Lookup] First record:`, firstRecord);
|
||||||
|
const email = firstRecord.emailtext || 'Email not found';
|
||||||
emailLookupCache[firstName] = email;
|
emailLookupCache[firstName] = email;
|
||||||
return email;
|
return email;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to lookup email for "${firstName}":`, error);
|
console.error(`Failed to lookup email for "${firstName}":`, error);
|
||||||
|
console.error(`Error response:`, error.response?.data);
|
||||||
return 'Error loading email';
|
return 'Error loading email';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,8 +89,6 @@ window.setupUserEmailLookup = function(pb, containerSelector) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✓ User Email Lookup initialized for: ${containerSelector}`);
|
console.log(`✓ User Email Lookup initialized for: ${containerSelector}`);
|
||||||
|
|
||||||
// Observer: Watch for new capsule additions
|
|
||||||
const observer = new MutationObserver((mutations) => {
|
const observer = new MutationObserver((mutations) => {
|
||||||
mutations.forEach((mutation) => {
|
mutations.forEach((mutation) => {
|
||||||
mutation.addedNodes.forEach((node) => {
|
mutation.addedNodes.forEach((node) => {
|
||||||
@@ -115,11 +128,14 @@ function attachEmailLookupTooltip(pb, capsuleEl) {
|
|||||||
const firstName = nameSpan.textContent.trim();
|
const firstName = nameSpan.textContent.trim();
|
||||||
if (!firstName) return;
|
if (!firstName) return;
|
||||||
|
|
||||||
|
console.log(`[Email Lookup] Attached to user: ${firstName}`);
|
||||||
|
|
||||||
// Mark as processed
|
// Mark as processed
|
||||||
nameSpan.setAttribute('data-email-loaded', 'true');
|
nameSpan.setAttribute('data-email-loaded', 'true');
|
||||||
|
|
||||||
// Attach hover listener
|
// Attach hover listener
|
||||||
capsuleEl.addEventListener('mouseenter', async () => {
|
capsuleEl.addEventListener('mouseenter', async () => {
|
||||||
|
console.log(`[Email Lookup] Hover on: ${firstName}`);
|
||||||
// If email already in title, don't query again
|
// If email already in title, don't query again
|
||||||
if (capsuleEl.title && !capsuleEl.title.startsWith('Loading')) {
|
if (capsuleEl.title && !capsuleEl.title.startsWith('Loading')) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user