Files
DiscordClone/packages/platform-web/src/index.js
2026-02-20 10:43:52 -06:00

142 lines
4.7 KiB
JavaScript

/**
* Web/Capacitor platform implementation.
* Uses Web Crypto API, localStorage, and Page Visibility API.
*/
import crypto from './crypto.js';
import session from './session.js';
import settings from './settings.js';
import idle from './idle.js';
import searchStorage from './searchStorage.js';
import SearchDatabase from '@discord-clone/shared/src/utils/SearchDatabase';
const searchDB = new SearchDatabase(searchStorage, crypto);
const webPlatform = {
crypto,
session,
settings,
idle,
links: {
openExternal(url) {
window.open(url, '_blank', 'noopener,noreferrer');
},
async fetchMetadata(url) {
// On web, metadata fetching would hit CORS. Use a Convex action or proxy instead.
// Return null to gracefully skip link previews that require server-side fetching.
return null;
},
},
screenCapture: {
async getScreenSources() {
// Web uses getDisplayMedia directly (no source picker like Electron).
// Return empty array; the web UI should call navigator.mediaDevices.getDisplayMedia() directly.
return [];
},
},
windowControls: null,
updates: null,
voiceService: null,
systemBars: null,
searchDB,
features: {
hasWindowControls: false,
hasScreenCapture: true,
hasNativeUpdates: false,
hasSearch: true,
hasVoiceService: false,
hasSystemBars: false,
},
};
// Detect Android/Capacitor and enable native APK updates
if (window.Capacitor?.isNativePlatform?.()) {
const YAML_URL = 'https://gitea.moyettes.com/Moyettes/DiscordClone/releases/download/latest/latest-android.yml';
const AppUpdater = window.Capacitor.Plugins.AppUpdater;
let apkUrl;
webPlatform.updates = {
async checkUpdate() {
try {
const response = await fetch(YAML_URL);
if (!response.ok) {
console.log('[UpdateCheck] Fetch failed:', response.status);
return { updateAvailable: false };
}
const yaml = await response.text();
const versionMatch = yaml.match(/^version:\s*(.+)$/m);
if (!versionMatch) {
console.log('[UpdateCheck] No version found in YAML');
return { updateAvailable: false };
}
const latestVersion = versionMatch[1].trim();
const pathMatch = yaml.match(/^path:\s*(.+)$/m);
const apkFilename = pathMatch ? pathMatch[1].trim() : `DiscordClone-v${latestVersion}.apk`;
apkUrl = `https://gitea.moyettes.com/Moyettes/DiscordClone/releases/download/latest/${apkFilename}`;
const { version: currentVersion } = await AppUpdater.getVersion();
console.log('[UpdateCheck] Latest:', latestVersion, '| Current:', currentVersion);
const latest = latestVersion.split('.').map(Number);
const current = currentVersion.split('.').map(Number);
let updateAvailable = false;
let updateType = 'patch';
for (let i = 0; i < Math.max(latest.length, current.length); i++) {
const l = latest[i] || 0;
const c = current[i] || 0;
if (l > c) {
updateAvailable = true;
updateType = i === 0 ? 'major' : i === 1 ? 'minor' : 'patch';
break;
}
if (l < c) break;
}
console.log('[UpdateCheck] Result:', updateAvailable ? updateType + ' update available' : 'up to date');
return { updateAvailable, updateType, latestVersion, currentVersion, apkUrl };
} catch (e) {
console.log('[UpdateCheck] Error:', e.message);
return { updateAvailable: false };
}
},
async installUpdate() {
await AppUpdater.downloadAndInstall({ url: apkUrl });
},
onDownloadProgress(callback) {
AppUpdater.addListener('downloadProgress', callback);
},
};
webPlatform.features.hasNativeUpdates = true;
webPlatform.features.hasScreenCapture = false;
// Native voice foreground service
const VoiceService = window.Capacitor.Plugins.VoiceService;
if (VoiceService) {
webPlatform.voiceService = {
async startService({ channelName, isMuted, isDeafened }) {
await VoiceService.startService({ channelName, isMuted, isDeafened });
},
async stopService() {
await VoiceService.stopService();
},
async updateNotification(opts) {
await VoiceService.updateNotification(opts);
},
addNotificationActionListener(callback) {
return VoiceService.addListener('voiceNotificationAction', callback);
},
};
webPlatform.features.hasVoiceService = true;
}
// Native system bar coloring
const SystemBars = window.Capacitor.Plugins.SystemBars;
if (SystemBars) {
webPlatform.systemBars = { setColors: (opts) => SystemBars.setColors(opts) };
webPlatform.features.hasSystemBars = true;
}
}
export default webPlatform;