mirror of
https://github.com/mudabbir-ahmad/Media-Aggregation-and-Sorting-System.git
synced 2026-10-07 19:40:20 +00:00
feat: migrate to Google Photos Picker API and update related documentation due to normal library api being depricated (kept for added media from app)
This commit is contained in:
1 parent
f6c61ab829
commit
5c096fbab4
5 files changed
+363
-201
No files matched your search
+103
-7
@@ -6,6 +6,7 @@ import { useFocusEffect } from '@react-navigation/native';
|
||||
import { useTheme } from '../services/ThemeContext';
|
||||
import DatabaseService from '../services/DatabaseService';
|
||||
import DeviceMediaService from '../services/DeviceMediaService';
|
||||
import GoogleAUTH from '../AUTH/GoogleAUTH';
|
||||
import NASAUTH from '../AUTH/NASAUTH';
|
||||
import TopBar from '../components/TopBar';
|
||||
import PhotoGrid from '../components/PhotoGrid';
|
||||
@@ -26,6 +27,8 @@ const HomeScreen = ({ navigation, route }) => {
|
||||
const [viewerVisible, setViewerVisible] = useState(false);
|
||||
const [deviceEndCursor, setDeviceEndCursor] = useState(null);
|
||||
const [deviceHasMore, setDeviceHasMore] = useState(false);
|
||||
const [googleNextPage, setGoogleNextPage] = useState(null);
|
||||
const [googleSessionId, setGoogleSessionId] = useState(null);
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
@@ -56,7 +59,7 @@ const HomeScreen = ({ navigation, route }) => {
|
||||
try {
|
||||
const newMedia = [];
|
||||
|
||||
// ─── Device photos (camera roll + Google Photos synced images) ───
|
||||
// ─── Device photos (local camera roll, screenshots, etc.) ───
|
||||
try {
|
||||
const deviceResult = await DeviceMediaService.getPhotos(100);
|
||||
if (deviceResult.mediaItems.length) {
|
||||
@@ -68,6 +71,59 @@ const HomeScreen = ({ navigation, route }) => {
|
||||
console.error('Device media fetch error:', err);
|
||||
}
|
||||
|
||||
// ─── Google Photos cloud (via Picker API — includes cloud-only photos) ───
|
||||
const googleAccount = linkedAccounts.find((a) => a.service_type === 'GOOGLE_PHOTOS');
|
||||
if (googleAccount) {
|
||||
try {
|
||||
const data = googleAccount.account_data || {};
|
||||
const googleAuth = new GoogleAUTH();
|
||||
googleAuth.accessToken = data.accessToken || null;
|
||||
googleAuth.refreshToken = data.refreshToken || null;
|
||||
googleAuth.expiresAt = data.expiresAt || 0;
|
||||
|
||||
let photos = await googleAuth.getPhotos(100, null, null);
|
||||
|
||||
// If the stored token has stale scopes (missing photospicker scope),
|
||||
// automatically re-authenticate to get a fresh token and retry.
|
||||
if (photos?.error?.code === 403) {
|
||||
console.log('Stored token missing Picker scope — re-authenticating…');
|
||||
try {
|
||||
await googleAuth.revokeAccess();
|
||||
const fresh = await googleAuth.authenticate();
|
||||
await DatabaseService.linkAccount(userId, 'GOOGLE_PHOTOS', {
|
||||
accessToken: fresh.accessToken,
|
||||
refreshToken: fresh.refreshToken || null,
|
||||
expiresAt: googleAuth.expiresAt,
|
||||
email: fresh.email || data.email || 'google-user',
|
||||
});
|
||||
photos = await googleAuth.getPhotos(100, null, null);
|
||||
} catch (authErr) {
|
||||
console.warn('Google re-auth failed:', authErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (photos?.error) {
|
||||
console.warn('Google Photos cloud fetch failed:', photos.error.message);
|
||||
} else {
|
||||
if (photos?.mediaItems?.length) newMedia.push(...photos.mediaItems);
|
||||
setGoogleNextPage(photos?.nextPageToken || null);
|
||||
setGoogleSessionId(photos?.sessionId || null);
|
||||
}
|
||||
|
||||
// Persist any refreshed tokens back to DB
|
||||
if (googleAuth.accessToken && googleAuth.accessToken !== data.accessToken) {
|
||||
await DatabaseService.linkAccount(userId, 'GOOGLE_PHOTOS', {
|
||||
accessToken: googleAuth.accessToken,
|
||||
refreshToken: googleAuth.refreshToken || data.refreshToken,
|
||||
expiresAt: googleAuth.expiresAt,
|
||||
email: data.email || 'google-user',
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Google Photos cloud fetch error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── NAS fetch ───
|
||||
const nasAccount = linkedAccounts.find((a) => a.service_type === 'NAS');
|
||||
if (nasAccount) {
|
||||
@@ -89,6 +145,7 @@ const HomeScreen = ({ navigation, route }) => {
|
||||
console.log('iCloud: placeholder — would fetch photos here');
|
||||
}
|
||||
|
||||
// Deduplicate by source_id (Google Photos cloud items may overlap with device items by filename)
|
||||
const unique = Array.from(
|
||||
new Map(newMedia.map((m) => [m.source_id || m.id || m.local_path, m])).values()
|
||||
);
|
||||
@@ -111,20 +168,59 @@ const HomeScreen = ({ navigation, route }) => {
|
||||
};
|
||||
|
||||
const loadMorePhotos = async () => {
|
||||
if (loadingMore || !deviceHasMore) return;
|
||||
if (loadingMore) return;
|
||||
// Load more from whichever source has more pages
|
||||
const canLoadDevice = deviceHasMore;
|
||||
const canLoadGoogle = !!googleNextPage;
|
||||
if (!canLoadDevice && !canLoadGoogle) return;
|
||||
|
||||
setLoadingMore(true);
|
||||
try {
|
||||
const result = await DeviceMediaService.getPhotos(100, deviceEndCursor);
|
||||
if (result.mediaItems.length) {
|
||||
const moreMedia = [];
|
||||
|
||||
// More device photos
|
||||
if (canLoadDevice) {
|
||||
const result = await DeviceMediaService.getPhotos(100, deviceEndCursor);
|
||||
if (result.mediaItems.length) moreMedia.push(...result.mediaItems);
|
||||
setDeviceEndCursor(result.endCursor);
|
||||
setDeviceHasMore(result.hasNextPage);
|
||||
}
|
||||
|
||||
// More Google Photos cloud photos (reuse the Picker session)
|
||||
if (canLoadGoogle) {
|
||||
const googleAccount = linkedAccounts.find((a) => a.service_type === 'GOOGLE_PHOTOS');
|
||||
if (googleAccount) {
|
||||
const data = googleAccount.account_data || {};
|
||||
const googleAuth = new GoogleAUTH();
|
||||
googleAuth.accessToken = data.accessToken || null;
|
||||
googleAuth.refreshToken = data.refreshToken || null;
|
||||
googleAuth.expiresAt = data.expiresAt || 0;
|
||||
|
||||
const photos = await googleAuth.getPhotos(100, googleNextPage, googleSessionId);
|
||||
if (photos?.mediaItems?.length) moreMedia.push(...photos.mediaItems);
|
||||
setGoogleNextPage(photos?.nextPageToken || null);
|
||||
if (photos?.sessionId) setGoogleSessionId(photos.sessionId);
|
||||
|
||||
// Persist refreshed tokens
|
||||
if (googleAuth.accessToken && googleAuth.accessToken !== data.accessToken) {
|
||||
await DatabaseService.linkAccount(userId, 'GOOGLE_PHOTOS', {
|
||||
accessToken: googleAuth.accessToken,
|
||||
refreshToken: googleAuth.refreshToken || data.refreshToken,
|
||||
expiresAt: googleAuth.expiresAt,
|
||||
email: data.email || 'google-user',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (moreMedia.length) {
|
||||
setAllMedia((prev) => {
|
||||
const combined = [...prev, ...result.mediaItems];
|
||||
const combined = [...prev, ...moreMedia];
|
||||
return Array.from(
|
||||
new Map(combined.map((m) => [m.source_id || m.id || m.local_path, m])).values()
|
||||
);
|
||||
});
|
||||
}
|
||||
setDeviceEndCursor(result.endCursor);
|
||||
setDeviceHasMore(result.hasNextPage);
|
||||
} catch (err) {
|
||||
console.error('Error loading more photos:', err);
|
||||
} finally {
|
||||
|
||||
Reference in new issue
Block a user