feat: enhance Google Photos integration; add platform-specific handling and user alerts for picker errors

This commit is contained in:
bobbert committed 2026-04-16 19:57:43 +01:00
1 parent 882571d53b
commit 183e33cbfa
2 files changed
+81 -36

No files matched your search

+67 -36
View File
@@ -5,6 +5,7 @@ import { File, Directory, Paths } from 'expo-file-system';
import { AuthServiceBase } from './AuthServiceBase';
import MediaMetadataService from '../services/MediaMetadataService';
import { logError } from '../services/ErrorHandler';
import { Platform, Linking } from 'react-native';
WebBrowser.maybeCompleteAuthSession();
@@ -378,50 +379,80 @@ class GoogleAUTH extends AuthServiceBase {
return results;
}
async pickPhotos() {
try {
console.log('Creating Google Photos Picker session…');
const session = await this._createPickerSession();
async pickPhotos() {
try {
console.log('Creating Google Photos Picker session…');
const session = await this._createPickerSession();
if (session?.error) {
return this._buildPickerError(session.error.code, session.error.message);
}
if (!session?.id || !session?.pickerUri) {
return this._buildPickerError(0, 'Failed to create Picker session');
}
if (session?.error) {
return this._buildPickerError(session.error.code, session.error.message);
}
if (!session?.id || !session?.pickerUri) {
return this._buildPickerError(0, 'Failed to create Picker session');
}
console.log('Picker session created:', session.id);
console.log('Opening picker for user selection…');
console.log('Picker session created:', session.id);
console.log('Opening picker for user selection…');
console.log('Platform:', Platform.OS);
console.log('Picker URI:', session.pickerUri.substring(0, 100) + '...');
await WebBrowser.openBrowserAsync(session.pickerUri, {
dismissButtonStyle: 'done',
presentationStyle: 'pageSheet',
});
// Try WebBrowser first
let browserOpened = false;
try {
const result = await WebBrowser.openBrowserAsync(session.pickerUri, {
dismissButtonStyle: 'done',
presentationStyle: 'pageSheet',
});
browserOpened = true;
console.log('WebBrowser closed with result:', result?.type);
} catch (webBrowserErr) {
console.warn('WebBrowser failed:', webBrowserErr.message);
// Fallback: try direct URL linking on Android APK builds
if (Platform.OS === 'android') {
console.log('Attempting fallback with Linking.openURL...');
try {
const canOpen = await Linking.canOpenURL(session.pickerUri);
console.log('Can open URL:', canOpen);
if (canOpen) {
await Linking.openURL(session.pickerUri);
browserOpened = true;
} else {
console.warn('System cannot open picker URL');
}
} catch (linkErr) {
console.warn('Linking.openURL failed:', linkErr.message);
}
}
}
console.log('Browser closed — polling for selected photos…');
if (!browserOpened) {
return this._buildPickerError(0, 'Failed to open browser/picker. Please check your internet connection and try again.');
}
const ready = await this._waitForSession(session.id);
if (!ready) {
return this._buildPickerError(0, 'No photos were selected or the picker was dismissed', session.id);
}
console.log('Browser closed — polling for selected photos…');
const result = await this._fetchPickerMediaItems(session.id, 100, null);
let mediaItems = this._normalisePickerItems(result.mediaItems);
const ready = await this._waitForSession(session.id);
if (!ready) {
return this._buildPickerError(0, 'No photos were selected or the picker was dismissed', session.id);
}
console.log(`Picker returned ${mediaItems.length} selected items, downloading…`);
mediaItems = await this.downloadPickerPhotos(mediaItems);
console.log(`Downloaded ${mediaItems.filter((m) => m.local_path).length} photos to cache`);
const result = await this._fetchPickerMediaItems(session.id, 100, null);
let mediaItems = this._normalisePickerItems(result.mediaItems);
return {
mediaItems,
nextPageToken: result.nextPageToken || null,
sessionId: session.id,
};
} catch (error) {
logError('Pick Photos', error);
return EMPTY_PICK_RESULT;
}
}
console.log(`Picker returned ${mediaItems.length} selected items, downloading…`);
mediaItems = await this.downloadPickerPhotos(mediaItems);
console.log(`Downloaded ${mediaItems.filter((m) => m.local_path).length} photos to cache`);
return {
mediaItems,
nextPageToken: result.nextPageToken || null,
sessionId: session.id,
};
} catch (error) {
logError('Pick Photos', error);
return EMPTY_PICK_RESULT;
}
}
async getMorePhotos(pageSize = 100, pageToken, sessionId) {
try {
+14
View File
@@ -272,6 +272,7 @@ const HomeScreen = ({ navigation, route }) => {
const googleAccount = linkedAccounts.find((a) => a.service_type === 'GOOGLE_PHOTOS');
if (!googleAccount) {
console.warn('No Google account linked');
Alert.alert('Error', 'No Google account linked. Please link your Google account in Settings.');
return;
}
@@ -297,6 +298,11 @@ const HomeScreen = ({ navigation, route }) => {
if (result?.error) {
logError('Import Google Photos', result.error.message);
Alert.alert(
'Google Photos Picker Error',
result.error.message || 'Failed to open Google Photos picker. Please try again.',
[{ text: 'OK' }]
);
} else if (result?.mediaItems?.length) {
await Promise.all(result.mediaItems.map((item) => DatabaseService.saveMediaItem(userId, item)));
@@ -306,6 +312,9 @@ const HomeScreen = ({ navigation, route }) => {
setAllMedia((prev) => mergeMediaAcrossServices([...prev, ...result.mediaItems]));
console.log(`Imported ${result.mediaItems.length} photos from Google`);
Alert.alert('Success', `Imported ${result.mediaItems.length} photos from Google Photos`);
} else {
console.log('No photos selected');
}
if (googleAuth.accessToken && googleAuth.accessToken !== data.accessToken) {
@@ -318,6 +327,11 @@ const HomeScreen = ({ navigation, route }) => {
}
} catch (err) {
logError('Import Google Photos', err);
Alert.alert(
'Error',
`Failed to import from Google Photos: ${err.message}`,
[{ text: 'OK' }]
);
} finally {
setImporting(false);
}