feat: Introduce multi-platform architecture for Electron and Web clients with shared UI components, Convex backend for messaging, and integrated search functionality.
Some checks failed
Build and Release / build-and-release (push) Has been cancelled
Some checks failed
Build and Release / build-and-release (push) Has been cancelled
This commit is contained in:
@@ -603,6 +603,43 @@ app.whenReady().then(async () => {
|
||||
});
|
||||
});
|
||||
|
||||
// --- Search DB file storage ---
|
||||
const SEARCH_DIR = path.join(app.getPath('userData'), 'search');
|
||||
|
||||
ipcMain.handle('search-db-load', (event, userId) => {
|
||||
try {
|
||||
const filePath = path.join(SEARCH_DIR, `search-${userId}.db.enc`);
|
||||
if (!fs.existsSync(filePath)) return null;
|
||||
return new Uint8Array(fs.readFileSync(filePath));
|
||||
} catch (err) {
|
||||
console.error('Search DB load error:', err.message);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('search-db-save', (event, userId, data) => {
|
||||
try {
|
||||
if (!fs.existsSync(SEARCH_DIR)) fs.mkdirSync(SEARCH_DIR, { recursive: true });
|
||||
const filePath = path.join(SEARCH_DIR, `search-${userId}.db.enc`);
|
||||
fs.writeFileSync(filePath, Buffer.from(data));
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('Search DB save error:', err.message);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('search-db-clear', (event, userId) => {
|
||||
try {
|
||||
const filePath = path.join(SEARCH_DIR, `search-${userId}.db.enc`);
|
||||
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('Search DB clear error:', err.message);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// AFK voice channel: expose system idle time to renderer
|
||||
ipcMain.handle('get-system-idle-time', () => powerMonitor.getSystemIdleTime());
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ contextBridge.exposeInMainWorld('sessionPersistence', {
|
||||
clear: () => ipcRenderer.invoke('clear-session'),
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('searchStorage', {
|
||||
load: (userId) => ipcRenderer.invoke('search-db-load', userId),
|
||||
save: (userId, data) => ipcRenderer.invoke('search-db-save', userId, data),
|
||||
clear: (userId) => ipcRenderer.invoke('search-db-clear', userId),
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('idleAPI', {
|
||||
onIdleStateChanged: (callback) => ipcRenderer.on('idle-state-changed', (_event, data) => callback(data)),
|
||||
removeIdleStateListener: () => ipcRenderer.removeAllListeners('idle-state-changed'),
|
||||
|
||||
@@ -6,6 +6,7 @@ import { PlatformProvider } from '@discord-clone/shared/src/platform';
|
||||
import App from '@discord-clone/shared/src/App';
|
||||
import { ThemeProvider } from '@discord-clone/shared/src/contexts/ThemeContext';
|
||||
import { VoiceProvider } from '@discord-clone/shared/src/contexts/VoiceContext';
|
||||
import { SearchProvider } from '@discord-clone/shared/src/contexts/SearchContext';
|
||||
import { UpdateProvider } from '@discord-clone/shared/src/components/UpdateBanner';
|
||||
import TitleBar from '@discord-clone/shared/src/components/TitleBar';
|
||||
import electronPlatform from './platform';
|
||||
@@ -20,12 +21,14 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<ThemeProvider>
|
||||
<UpdateProvider>
|
||||
<ConvexProvider client={convex}>
|
||||
<VoiceProvider>
|
||||
<TitleBar />
|
||||
<HashRouter>
|
||||
<App />
|
||||
</HashRouter>
|
||||
</VoiceProvider>
|
||||
<SearchProvider>
|
||||
<VoiceProvider>
|
||||
<TitleBar />
|
||||
<HashRouter>
|
||||
<App />
|
||||
</HashRouter>
|
||||
</VoiceProvider>
|
||||
</SearchProvider>
|
||||
</ConvexProvider>
|
||||
</UpdateProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
* Electron platform implementation.
|
||||
* Delegates to the window.* APIs exposed by preload.cjs.
|
||||
*/
|
||||
import SearchDatabase from '@discord-clone/shared/src/utils/SearchDatabase';
|
||||
|
||||
const searchDB = new SearchDatabase(
|
||||
window.searchStorage,
|
||||
{
|
||||
encryptData: (data, key) => window.cryptoAPI.encryptData(data, key),
|
||||
decryptData: (ct, key, iv, tag) => window.cryptoAPI.decryptData(ct, key, iv, tag),
|
||||
}
|
||||
);
|
||||
|
||||
const electronPlatform = {
|
||||
crypto: {
|
||||
generateKeys: () => window.cryptoAPI.generateKeys(),
|
||||
@@ -46,10 +56,12 @@ const electronPlatform = {
|
||||
updates: {
|
||||
checkUpdate: () => window.updateAPI.checkFlatpakUpdate(),
|
||||
},
|
||||
searchDB,
|
||||
features: {
|
||||
hasWindowControls: true,
|
||||
hasScreenCapture: true,
|
||||
hasNativeUpdates: true,
|
||||
hasSearch: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { PlatformProvider } from '@discord-clone/shared/src/platform';
|
||||
import App from '@discord-clone/shared/src/App';
|
||||
import { ThemeProvider } from '@discord-clone/shared/src/contexts/ThemeContext';
|
||||
import { VoiceProvider } from '@discord-clone/shared/src/contexts/VoiceContext';
|
||||
import { SearchProvider } from '@discord-clone/shared/src/contexts/SearchContext';
|
||||
import webPlatform from '@discord-clone/platform-web';
|
||||
import '@discord-clone/shared/src/styles/themes.css';
|
||||
import '@discord-clone/shared/src/index.css';
|
||||
@@ -17,11 +18,13 @@ ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<PlatformProvider platform={webPlatform}>
|
||||
<ThemeProvider>
|
||||
<ConvexProvider client={convex}>
|
||||
<VoiceProvider>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</VoiceProvider>
|
||||
<SearchProvider>
|
||||
<VoiceProvider>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</VoiceProvider>
|
||||
</SearchProvider>
|
||||
</ConvexProvider>
|
||||
</ThemeProvider>
|
||||
</PlatformProvider>
|
||||
|
||||
Reference in New Issue
Block a user