Parte 2: Sistema de Datos y Almacenamiento
Objetivo
Implementar el sistema de almacenamiento de datos con comunicación IPC entre proceso principal y renderer.
Paso 1: Actualizar main.js con funciones IPC
Reemplaza el contenido de main.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
const { app, BrowserWindow, ipcMain, Menu, dialog } = require('electron');
const path = require('path');
const fs = require('fs').promises;
let mainWindow;
const DATA_FILE = path.join(__dirname, 'renderer', 'data', 'reminders.json');
const PREFERENCES_FILE = path.join(__dirname, 'preferences.json');
function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 900,
    minWidth: 1200,
    minHeight: 900,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.js')
    },
    show: false
  });
  mainWindow.loadFile('renderer/views/index.html');
  mainWindow.once('ready-to-show', () => {
    mainWindow.show();
  });
}
// IPC Handlers para recordatorios
ipcMain.handle('get-reminders', async () => {
  try {
    const data = await fs.readFile(DATA_FILE, 'utf-8');
    return JSON.parse(data);
  } catch (error) {
    return [];
  }
});
ipcMain.handle('save-reminders', async (event, reminders) => {
  try {
    await fs.writeFile(DATA_FILE, JSON.stringify(reminders, null, 2));
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
});
// IPC Handlers para preferencias
ipcMain.handle('get-preference', async (event, key) => {
  try {
    const data = await fs.readFile(PREFERENCES_FILE, 'utf-8');
    const preferences = JSON.parse(data);
    return preferences[key];
  } catch (error) {
    return null;
  }
});
ipcMain.handle('set-preference', async (event, key, value) => {
  try {
    let preferences = {};
    try {
      const data = await fs.readFile(PREFERENCES_FILE, 'utf-8');
      preferences = JSON.parse(data);
    } catch (error) {}
    
    preferences[key] = value;
    await fs.writeFile(PREFERENCES_FILE, JSON.stringify(preferences, null, 2));
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
});
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});
Paso 2: Actualizar preload.js
Reemplaza preload.js:
1
2
3
4
5
6
7
8
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
  getReminders: () => ipcRenderer.invoke('get-reminders'),
  saveReminders: (reminders) => ipcRenderer.invoke('save-reminders', reminders),
  getPreference: (key) => ipcRenderer.invoke('get-preference', key),
  setPreference: (key, value) => ipcRenderer.invoke('set-preference', key, value)
});
Paso 3: Crear archivo de variables globales
Crea renderer/js/variables.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Variables globales
let reminders = [];
let editingId = null;
let currentTheme = 'light';
let sidebarCollapsed = false;
// Claves para preferencias
const THEME_KEY = 'theme';
const SIDEBAR_KEY = 'sidebarCollapsed';
// Elementos del DOM
let reminderForm, remindersList, emptyState, searchInput, clearSearchBtn;
let darkModeToggle, submitBtn, cancelBtn, sidebar;
let deleteModal, confirmDeleteBtn, cancelDeleteBtn, modalClose, modalReminderTitle;
let reminderToDelete = null;
Paso 4: Crear sistema de almacenamiento
Crea renderer/js/storage.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// Cargar preferencias
async function loadPreferences() {
    try {
        currentTheme = await window.electronAPI.getPreference(THEME_KEY) || 'light';
        const sidebarPref = await window.electronAPI.getPreference(SIDEBAR_KEY);
        sidebarCollapsed = sidebarPref === true || sidebarPref === 'true';
    } catch (error) {
        currentTheme = 'light';
        sidebarCollapsed = false;
    }
}
// Guardar preferencia
async function savePreference(key, value) {
    try {
        await window.electronAPI.setPreference(key, value);
    } catch (error) {
        console.error('Error guardando preferencia:', error);
    }
}
// Cargar recordatorios
async function loadReminders() {
    try {
        reminders = await window.electronAPI.getReminders();
        renderReminders();
    } catch (error) {
        console.error('Error al cargar recordatorios:', error);
        reminders = [];
    }
}
// Guardar recordatorios
async function saveReminders() {
    try {
        const result = await window.electronAPI.saveReminders(reminders);
        return result.success;
    } catch (error) {
        console.error('Error al guardar recordatorios:', error);
        return false;
    }
}
Paso 5: Crear archivo de datos inicial
Crea renderer/data/reminders.json:
1
[]
Paso 6: Crear archivo de inicialización
Crea renderer/js/init.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Inicializar aplicación
async function initApp() {
    console.log('Iniciando aplicación...');
    
    // Cargar preferencias
    await loadPreferences();
    
    // Aplicar tema
    applyTheme();
    
    // Cargar recordatorios
    await loadReminders();
    
    console.log('Aplicación iniciada correctamente');
}
// Aplicar tema
function applyTheme() {
    if (currentTheme === 'dark') {
        document.body.classList.add('dark-mode');
    } else {
        document.body.classList.remove('dark-mode');
    }
}
// Inicializar cuando el DOM esté listo
document.addEventListener('DOMContentLoaded', initApp);
Paso 7: Actualizar index.html
Actualiza renderer/views/index.html:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Recordatorios App</title>
    <link rel="stylesheet" href="../assets/css/styles.css">
</head>
<body>
    <div class="main-container">
        <h1>Aplicación de Recordatorios</h1>
        <div id="app">
            <div id="remindersList"></div>
        </div>
    </div>
    <script src="../js/variables.js"></script>
    <script src="../js/storage.js"></script>
    <script src="../js/init.js"></script>
</body>
</html>
Paso 8: Crear función temporal de renderizado
Agrega al final de renderer/js/storage.js:
1
2
3
4
5
6
7
8
9
10
11
// Función temporal de renderizado (se mejorará en Parte 3)
function renderReminders() {
    const container = document.getElementById('remindersList');
    if (!container) return;
    
    if (reminders.length === 0) {
        container.innerHTML = '<p>No hay recordatorios todavía.</p>';
    } else {
        container.innerHTML = `<p>Tienes ${reminders.length} recordatorio(s)</p>`;
    }
}
Paso 9: Probar
1
npm start
Abre la consola de desarrollador (F12) y ejecuta:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Crear un recordatorio de prueba
reminders.push({
    id: Date.now(),
    title: 'Recordatorio de prueba',
    description: 'Esto es una prueba',
    category: 'personal',
    priority: 'media',
    completed: false,
    createdAt: new Date().toISOString()
});
// Guardar
await saveReminders();
// Recargar
await loadReminders();
Resultado esperado
✅ Sistema de almacenamiento IPC funcionando ✅ Datos persistentes en JSON ✅ Sistema de preferencias operativo ✅ Carga y guardado de recordatorios
Siguiente paso
En la Parte 3, crearemos la interfaz de usuario principal con el formulario de recordatorios.
