All checks were successful
Build and Release / build-and-release (push) Successful in 13m4s
142 lines
4.7 KiB
JavaScript
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;
|