feat: Implement Android share functionality and background sync
- Add ShareRoomPicker component for selecting rooms to share Android intents. - Introduce AndroidShare utility for handling shared files and payloads from Android. - Implement background synchronization with UnifiedPush for Android notifications. - Enhance notification handling in Tauri and Capacitor environments. - Update TypeScript configuration for better module resolution. - Create Capacitor configuration file for Android integration. - Add SVG icon for notifications. - Refactor media URL authentication checks in matrix utility.
This commit is contained in:
@@ -116,6 +116,9 @@ export const isYouTubeStreamingAvailable = (): boolean => {
|
||||
/** Callback for notification tap actions */
|
||||
let notificationTapCallback: ((path: string) => void) | null = null;
|
||||
|
||||
const ANDROID_NOTIFICATION_SMALL_ICON = 'ic_stat_paarrot';
|
||||
const ANDROID_NOTIFICATION_ICON_COLOR = '#FF8A00';
|
||||
|
||||
/**
|
||||
* Bring the Tauri window to the front and focus it
|
||||
*/
|
||||
@@ -190,6 +193,21 @@ export const setupNotificationTapListener = async (onTap: (path: string) => void
|
||||
console.warn('Failed to set up Tauri notification tap listener:', err);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCapacitorNative()) {
|
||||
try {
|
||||
const { LocalNotifications } = await import('@capacitor/local-notifications');
|
||||
await LocalNotifications.addListener('localNotificationActionPerformed', async (event: any) => {
|
||||
await focusWindow();
|
||||
const path = event?.notification?.extra?.path;
|
||||
if (path && typeof path === 'string' && notificationTapCallback) {
|
||||
notificationTapCallback(path);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn('Failed to set up Capacitor notification tap listener:', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -204,6 +222,15 @@ export const isTauri = (): boolean =>
|
||||
export const isElectron = (): boolean =>
|
||||
typeof window !== 'undefined' && 'electron' in window;
|
||||
|
||||
/**
|
||||
* Check if we're running inside a Capacitor native app
|
||||
*/
|
||||
export const isCapacitorNative = (): boolean => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
const cap = (window as any).Capacitor;
|
||||
return Boolean(cap?.isNativePlatform?.() || cap?.getPlatform?.() === 'android' || cap?.getPlatform?.() === 'ios');
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we're running on a mobile platform (Android/iOS)
|
||||
*/
|
||||
@@ -305,6 +332,78 @@ const ensureNotificationChannel = async (): Promise<void> => {
|
||||
}
|
||||
};
|
||||
|
||||
const ensureCapacitorNotificationChannel = async (): Promise<void> => {
|
||||
if (notificationChannelCreated || !isAndroid()) return;
|
||||
|
||||
try {
|
||||
const { LocalNotifications } = await import('@capacitor/local-notifications');
|
||||
await LocalNotifications.createChannel({
|
||||
id: 'messages',
|
||||
name: 'Messages',
|
||||
description: 'Message notifications from Matrix',
|
||||
importance: 5,
|
||||
sound: 'default',
|
||||
visibility: 1,
|
||||
vibration: true,
|
||||
lights: true,
|
||||
});
|
||||
notificationChannelCreated = true;
|
||||
} catch (err) {
|
||||
console.warn('Failed to create Capacitor notification channel:', err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Request notification permission across Electron, Tauri, Capacitor, and browser
|
||||
*/
|
||||
export const requestSystemNotificationPermission = async (): Promise<boolean> => {
|
||||
if (isElectron() || (isTauri() && !isElectron())) {
|
||||
if (!('Notification' in window)) return false;
|
||||
const permission = await Notification.requestPermission();
|
||||
return permission === 'granted';
|
||||
}
|
||||
|
||||
if (isCapacitorNative()) {
|
||||
try {
|
||||
const { LocalNotifications } = await import('@capacitor/local-notifications');
|
||||
let perm = await LocalNotifications.checkPermissions();
|
||||
if (perm.display !== 'granted') {
|
||||
perm = await LocalNotifications.requestPermissions();
|
||||
}
|
||||
return perm.display === 'granted';
|
||||
} catch (err) {
|
||||
console.warn('Capacitor notification permission request failed:', err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!('Notification' in window)) return false;
|
||||
const permission = await Notification.requestPermission();
|
||||
return permission === 'granted';
|
||||
};
|
||||
|
||||
export const getSystemNotificationPermissionState = async (): Promise<PermissionState> => {
|
||||
if (isCapacitorNative()) {
|
||||
try {
|
||||
const { LocalNotifications } = await import('@capacitor/local-notifications');
|
||||
const perm = await LocalNotifications.checkPermissions();
|
||||
return perm.display === 'granted' ? 'granted' : 'prompt';
|
||||
} catch (err) {
|
||||
console.warn('Failed to check Capacitor notification permission:', err);
|
||||
return 'denied';
|
||||
}
|
||||
}
|
||||
|
||||
if ('Notification' in window) {
|
||||
if (window.Notification.permission === 'default') {
|
||||
return 'prompt';
|
||||
}
|
||||
return window.Notification.permission;
|
||||
}
|
||||
|
||||
return 'denied';
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a notification using Tauri's notification plugin
|
||||
* Falls back to browser Notification API if not in Tauri
|
||||
@@ -369,6 +468,38 @@ export const sendNotification = async (options: {
|
||||
console.warn('Tauri notification failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
if (isCapacitorNative()) {
|
||||
try {
|
||||
const { LocalNotifications } = await import('@capacitor/local-notifications');
|
||||
let perm = await LocalNotifications.checkPermissions();
|
||||
if (perm.display !== 'granted') {
|
||||
perm = await LocalNotifications.requestPermissions();
|
||||
}
|
||||
|
||||
if (perm.display === 'granted') {
|
||||
await ensureCapacitorNotificationChannel();
|
||||
|
||||
const id = Math.floor(Date.now() % 2147483647);
|
||||
await LocalNotifications.schedule({
|
||||
notifications: [
|
||||
{
|
||||
id,
|
||||
title,
|
||||
body,
|
||||
channelId: isAndroid() ? 'messages' : undefined,
|
||||
smallIcon: isAndroid() ? ANDROID_NOTIFICATION_SMALL_ICON : undefined,
|
||||
iconColor: isAndroid() ? ANDROID_NOTIFICATION_ICON_COLOR : undefined,
|
||||
extra: path ? { path } : undefined,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return;
|
||||
} catch (err) {
|
||||
console.warn('Capacitor notification failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to browser notification
|
||||
sendBrowserNotification({ title, body, icon, onClick });
|
||||
|
||||
Reference in New Issue
Block a user