tududi/frontend/utils/inboxService.ts
Chris f9b21dff0a
Fix today race condition (#75)
* Move frontend to root

* Fix backend issues

* Remove old routes

* Setup Dockerfile

* Fix today /tags multiplt requests issue

* Fix race condition on today's inbox widget

* Fix cors development issue

* Fix CORS for Dockerfile

* Fix dockerised settings for infinite loop

* Fix translation issues

* fixup! Fix translation issues

---------

Co-authored-by: Your Name <you@example.com>
2025-06-13 14:20:24 +03:00

184 lines
No EOL
5.5 KiB
TypeScript

import { InboxItem } from "../entities/InboxItem";
import { useStore } from "../store/useStore";
import { handleAuthResponse } from "./authUtils";
// API functions
export const fetchInboxItems = async (): Promise<InboxItem[]> => {
const response = await fetch('/api/inbox', {
credentials: 'include',
headers: {
'Accept': 'application/json',
},
});
await handleAuthResponse(response, 'Failed to fetch inbox items.');
const result = await response.json();
if (!Array.isArray(result)) {
throw new Error('Resulting inbox items are not an array.');
}
return result;
};
export const createInboxItem = async (content: string, source: string = 'tududi'): Promise<InboxItem> => {
const response = await fetch('/api/inbox', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ content, source }),
});
await handleAuthResponse(response, 'Failed to create inbox item.');
return await response.json();
};
export const updateInboxItem = async (itemId: number, content: string): Promise<InboxItem> => {
const response = await fetch(`/api/inbox/${itemId}`, {
method: 'PATCH',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ content }),
});
await handleAuthResponse(response, 'Failed to update inbox item.');
return await response.json();
};
export const processInboxItem = async (itemId: number): Promise<InboxItem> => {
const response = await fetch(`/api/inbox/${itemId}/process`, {
method: 'PATCH',
credentials: 'include',
headers: {
'Accept': 'application/json',
},
});
await handleAuthResponse(response, 'Failed to process inbox item.');
return await response.json();
};
export const deleteInboxItem = async (itemId: number): Promise<void> => {
const response = await fetch(`/api/inbox/${itemId}`, {
method: 'DELETE',
credentials: 'include',
headers: {
'Accept': 'application/json',
},
});
await handleAuthResponse(response, 'Failed to delete inbox item.');
};
// Track last check time to detect new items
let lastCheckTimestamp = Date.now();
// Store-aware functions
export const loadInboxItemsToStore = async (): Promise<void> => {
const inboxStore = useStore.getState().inboxStore;
// Only show loading for initial load
if (inboxStore.inboxItems.length === 0) {
inboxStore.setLoading(true);
}
try {
const items = await fetchInboxItems();
// Check for new items since last check
const currentItemIds = new Set(inboxStore.inboxItems.map(item => item.id));
const currentTime = Date.now();
// New telegram items
const newTelegramItems = items.filter(item =>
item.id &&
!currentItemIds.has(item.id) &&
item.source === 'telegram'
);
// Only show notifications if we have detected changes
if (inboxStore.inboxItems.length > 0 && newTelegramItems.length > 0) {
// Instead of trying to show toast directly (which won't work outside of React components),
// dispatch a custom event that the component can listen for and show toasts
// Get some minimal info about the items for the notification
const notificationData = {
count: newTelegramItems.length,
firstItemContent: newTelegramItems[0].content.substring(0, 30) +
(newTelegramItems[0].content.length > 30 ? '...' : '')
};
// Dispatch a custom event with the notification data
window.dispatchEvent(new CustomEvent('inboxItemsUpdated', {
detail: notificationData
}));
}
// Update state and timestamp
inboxStore.setInboxItems(items);
inboxStore.setError(false);
lastCheckTimestamp = currentTime;
} catch (error) {
console.error('Failed to load inbox items:', error);
inboxStore.setError(true);
} finally {
inboxStore.setLoading(false);
}
};
export const createInboxItemWithStore = async (content: string, source: string = 'tududi'): Promise<InboxItem> => {
const inboxStore = useStore.getState().inboxStore;
try {
const newItem = await createInboxItem(content, source);
inboxStore.addInboxItem(newItem);
return newItem;
} catch (error) {
console.error('Failed to create inbox item:', error);
throw error;
}
};
export const updateInboxItemWithStore = async (itemId: number, content: string): Promise<InboxItem> => {
const inboxStore = useStore.getState().inboxStore;
try {
const updatedItem = await updateInboxItem(itemId, content);
inboxStore.updateInboxItem(updatedItem);
return updatedItem;
} catch (error) {
console.error('Failed to update inbox item:', error);
throw error;
}
};
export const processInboxItemWithStore = async (itemId: number): Promise<InboxItem> => {
const inboxStore = useStore.getState().inboxStore;
try {
const processedItem = await processInboxItem(itemId);
inboxStore.removeInboxItem(itemId);
return processedItem;
} catch (error) {
console.error('Failed to process inbox item:', error);
throw error;
}
};
export const deleteInboxItemWithStore = async (itemId: number): Promise<void> => {
const inboxStore = useStore.getState().inboxStore;
try {
await deleteInboxItem(itemId);
inboxStore.removeInboxItem(itemId);
} catch (error) {
console.error('Failed to delete inbox item:', error);
throw error;
}
};