Initial release: DictIA v0.8.14-alpha (fork de Speakr, AGPL-3.0)
This commit is contained in:
327
static/js/composables/useRecordings.js
Normal file
327
static/js/composables/useRecordings.js
Normal file
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* Recordings composable
|
||||
* Handles recordings list, selection, and CRUD operations
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue';
|
||||
import { apiRequest } from '../utils/apiClient.js';
|
||||
|
||||
export function useRecordings() {
|
||||
// State
|
||||
const recordings = ref([]);
|
||||
const selectedRecording = ref(null);
|
||||
const isLoadingRecordings = ref(true);
|
||||
const globalError = ref(null);
|
||||
const currentView = ref('upload');
|
||||
const availableTags = ref([]);
|
||||
const selectedTagIds = ref([]);
|
||||
const showTagModal = ref(false);
|
||||
const showDeleteModal = ref(false);
|
||||
const recordingToDelete = ref(null);
|
||||
|
||||
// Computed
|
||||
const completedRecordings = computed(() => {
|
||||
return recordings.value.filter(r => r.status === 'COMPLETED');
|
||||
});
|
||||
|
||||
const processingRecordings = computed(() => {
|
||||
return recordings.value.filter(r => ['PENDING', 'PROCESSING', 'SUMMARIZING'].includes(r.status));
|
||||
});
|
||||
|
||||
const hasRecordings = computed(() => recordings.value.length > 0);
|
||||
|
||||
// Methods
|
||||
const loadRecordings = async (page = 1, filters = {}) => {
|
||||
globalError.value = null;
|
||||
isLoadingRecordings.value = true;
|
||||
|
||||
try {
|
||||
let endpoint = '/api/recordings';
|
||||
if (filters.archived) {
|
||||
endpoint = '/api/recordings/archived';
|
||||
} else if (filters.sharedWithMe) {
|
||||
endpoint = '/api/recordings/shared-with-me';
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
page: page.toString(),
|
||||
per_page: '25'
|
||||
});
|
||||
|
||||
if (filters.query) {
|
||||
params.set('q', filters.query.trim());
|
||||
}
|
||||
|
||||
const response = await fetch(`${endpoint}?${params}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || 'Failed to load recordings');
|
||||
}
|
||||
|
||||
const recordingsList = filters.archived || filters.sharedWithMe ? data : data.recordings;
|
||||
|
||||
if (!Array.isArray(recordingsList)) {
|
||||
throw new Error('Invalid response format');
|
||||
}
|
||||
|
||||
recordings.value = recordingsList;
|
||||
|
||||
// Restore last selected recording
|
||||
const lastRecordingId = localStorage.getItem('lastSelectedRecordingId');
|
||||
if (lastRecordingId && recordingsList.length > 0) {
|
||||
const recordingToSelect = recordingsList.find(r => r.id == lastRecordingId);
|
||||
if (recordingToSelect) {
|
||||
selectRecording(recordingToSelect);
|
||||
}
|
||||
}
|
||||
|
||||
return filters.archived || filters.sharedWithMe ? null : data.pagination;
|
||||
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
} finally {
|
||||
isLoadingRecordings.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const selectRecording = async (recording) => {
|
||||
if (!recording) return;
|
||||
|
||||
selectedRecording.value = recording;
|
||||
currentView.value = 'recording';
|
||||
localStorage.setItem('lastSelectedRecordingId', recording.id);
|
||||
|
||||
// Load full recording details if needed
|
||||
if (!recording.transcription && recording.status === 'COMPLETED') {
|
||||
try {
|
||||
const data = await apiRequest(`/api/recordings/${recording.id}`);
|
||||
Object.assign(selectedRecording.value, data);
|
||||
} catch (error) {
|
||||
console.error('Error loading recording details:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const deselectRecording = () => {
|
||||
selectedRecording.value = null;
|
||||
currentView.value = 'upload';
|
||||
localStorage.removeItem('lastSelectedRecordingId');
|
||||
};
|
||||
|
||||
const deleteRecording = async (recordingId) => {
|
||||
try {
|
||||
await apiRequest(`/api/recordings/${recordingId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
recordings.value = recordings.value.filter(r => r.id !== recordingId);
|
||||
|
||||
if (selectedRecording.value && selectedRecording.value.id === recordingId) {
|
||||
deselectRecording();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const archiveRecording = async (recordingId) => {
|
||||
try {
|
||||
await apiRequest(`/api/recordings/${recordingId}/archive`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
recordings.value = recordings.value.filter(r => r.id !== recordingId);
|
||||
|
||||
if (selectedRecording.value && selectedRecording.value.id === recordingId) {
|
||||
deselectRecording();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const unarchiveRecording = async (recordingId) => {
|
||||
try {
|
||||
await apiRequest(`/api/recordings/${recordingId}/unarchive`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
recordings.value = recordings.value.filter(r => r.id !== recordingId);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const updateRecording = async (recordingId, updates) => {
|
||||
try {
|
||||
const data = await apiRequest(`/api/recordings/${recordingId}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(updates)
|
||||
});
|
||||
|
||||
const index = recordings.value.findIndex(r => r.id === recordingId);
|
||||
if (index > -1) {
|
||||
Object.assign(recordings.value[index], data.recording || data);
|
||||
}
|
||||
|
||||
if (selectedRecording.value && selectedRecording.value.id === recordingId) {
|
||||
Object.assign(selectedRecording.value, data.recording || data);
|
||||
}
|
||||
|
||||
return data.recording || data;
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const regenerateSummary = async (recordingId, customPrompt = null) => {
|
||||
try {
|
||||
const body = customPrompt ? { custom_prompt: customPrompt } : {};
|
||||
const data = await apiRequest(`/api/recordings/${recordingId}/regenerate-summary`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
|
||||
if (selectedRecording.value && selectedRecording.value.id === recordingId) {
|
||||
selectedRecording.value.status = 'SUMMARIZING';
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const loadTags = async () => {
|
||||
try {
|
||||
const data = await apiRequest('/api/tags');
|
||||
availableTags.value = data;
|
||||
} catch (error) {
|
||||
console.error('Error loading tags:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const addTagToRecording = async (recordingId, tagId) => {
|
||||
try {
|
||||
const data = await apiRequest(`/api/recordings/${recordingId}/tags`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ tag_id: tagId })
|
||||
});
|
||||
|
||||
if (selectedRecording.value && selectedRecording.value.id === recordingId) {
|
||||
selectedRecording.value.tags = data.tags || [];
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const removeTagFromRecording = async (recordingId, tagId) => {
|
||||
try {
|
||||
await apiRequest(`/api/recordings/${recordingId}/tags/${tagId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (selectedRecording.value && selectedRecording.value.id === recordingId) {
|
||||
selectedRecording.value.tags = selectedRecording.value.tags.filter(t => t.id !== tagId);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
globalError.value = error.message;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const toggleHighlight = async (recordingId) => {
|
||||
const recording = recordings.value.find(r => r.id === recordingId);
|
||||
if (!recording) return;
|
||||
|
||||
const newValue = !recording.is_highlighted;
|
||||
|
||||
try {
|
||||
await updateRecording(recordingId, { is_highlighted: newValue });
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const setGlobalError = (message) => {
|
||||
globalError.value = message;
|
||||
};
|
||||
|
||||
const clearGlobalError = () => {
|
||||
globalError.value = null;
|
||||
};
|
||||
|
||||
const confirmDelete = (recording) => {
|
||||
recordingToDelete.value = recording;
|
||||
showDeleteModal.value = true;
|
||||
};
|
||||
|
||||
const cancelDelete = () => {
|
||||
recordingToDelete.value = null;
|
||||
showDeleteModal.value = false;
|
||||
};
|
||||
|
||||
const executeDelete = async () => {
|
||||
if (recordingToDelete.value) {
|
||||
await deleteRecording(recordingToDelete.value.id);
|
||||
cancelDelete();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
// State
|
||||
recordings,
|
||||
selectedRecording,
|
||||
isLoadingRecordings,
|
||||
globalError,
|
||||
currentView,
|
||||
availableTags,
|
||||
selectedTagIds,
|
||||
showTagModal,
|
||||
showDeleteModal,
|
||||
recordingToDelete,
|
||||
|
||||
// Computed
|
||||
completedRecordings,
|
||||
processingRecordings,
|
||||
hasRecordings,
|
||||
|
||||
// Methods
|
||||
loadRecordings,
|
||||
selectRecording,
|
||||
deselectRecording,
|
||||
deleteRecording,
|
||||
archiveRecording,
|
||||
unarchiveRecording,
|
||||
updateRecording,
|
||||
regenerateSummary,
|
||||
loadTags,
|
||||
addTagToRecording,
|
||||
removeTagFromRecording,
|
||||
toggleHighlight,
|
||||
setGlobalError,
|
||||
clearGlobalError,
|
||||
confirmDelete,
|
||||
cancelDelete,
|
||||
executeDelete
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user