update: implement event-based photo storage, add event name validation, version apks
This commit is contained in:
+53
-12
@@ -1,6 +1,5 @@
|
||||
import { getPermissionsAsync, requestPermissionsAsync, createAssetAsync, getAlbumAsync, createAlbumAsync, addAssetsToAlbumAsync } from 'expo-media-library';
|
||||
|
||||
const ALBUM_NAME = 'Schnappix';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
|
||||
/**
|
||||
* Request necessary media library permissions.
|
||||
@@ -20,44 +19,86 @@ export async function requestStoragePermission(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getFormattedDateTime(): string {
|
||||
const d = new Date();
|
||||
const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||
const time = `${String(d.getHours()).padStart(2, '0')}-${String(d.getMinutes()).padStart(2, '0')}-${String(d.getSeconds()).padStart(2, '0')}`;
|
||||
return `${date}_${time}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a local image file directly to the public DCIM gallery inside the "Schnappix" album.
|
||||
* Saves a local image file directly to the public DCIM/Pictures gallery inside a structured album.
|
||||
*
|
||||
* @param localUri The local file URI of the image (temp cached file).
|
||||
* @param type 'Raw' for original photos, 'Collage' for final prints.
|
||||
* @param eventName The name of the event to use for folder and file naming.
|
||||
* @returns Promise<string> The permanent URI of the saved asset in the gallery.
|
||||
*/
|
||||
export async function saveToGallery(localUri: string): Promise<string> {
|
||||
export async function saveToGallery(localUri: string, type: 'Raw' | 'Collage', eventName: string): Promise<string> {
|
||||
const hasPermission = await requestStoragePermission();
|
||||
if (!hasPermission) {
|
||||
throw new Error('Storage permission not granted. Cannot save photo to gallery.');
|
||||
}
|
||||
|
||||
// 1. Create a media asset from the local file
|
||||
const asset = await createAssetAsync(localUri);
|
||||
// Clean event name for file system usage
|
||||
const safeEventName = eventName.replace(/[^a-zA-Z0-9_-]/g, '').trim() || 'Event';
|
||||
const timestamp = getFormattedDateTime();
|
||||
const fileExtension = localUri.split('.').pop() || 'jpg';
|
||||
|
||||
// Format: EventName_YYYY-MM-DD_HH-mm-ss_Type.jpg
|
||||
const newFileName = `${safeEventName}_${timestamp}_${type}.${fileExtension}`;
|
||||
const newLocalUri = `${FileSystem.cacheDirectory}${newFileName}`;
|
||||
|
||||
// Copy the file to the new location to force the filename
|
||||
await FileSystem.copyAsync({
|
||||
from: localUri,
|
||||
to: newLocalUri,
|
||||
});
|
||||
|
||||
// 1. Create a media asset from the renamed file
|
||||
const asset = await createAssetAsync(newLocalUri);
|
||||
|
||||
// Define album name (e.g. "Schnappix/EventName/Originale")
|
||||
const subFolder = type === 'Raw' ? 'Originale' : 'Collagen';
|
||||
const ALBUM_NAME = `Schnappix/${safeEventName}/${subFolder}`;
|
||||
|
||||
try {
|
||||
// 2. Check if the "Schnappix" album already exists
|
||||
// 2. Check if the album already exists
|
||||
const album = await getAlbumAsync(ALBUM_NAME);
|
||||
|
||||
if (!album) {
|
||||
// 3. If it doesn't exist, create it with our asset
|
||||
// copyAsset=true completely bypasses the Android 11+ OS prompt!
|
||||
await createAlbumAsync(ALBUM_NAME, asset, true);
|
||||
console.log(`Created new album "${ALBUM_NAME}" and copied photo.`);
|
||||
} else {
|
||||
// 4. If it exists, add our asset to it
|
||||
// copyAsset=true completely bypasses the Android 11+ OS prompt!
|
||||
await addAssetsToAlbumAsync([asset], album, true);
|
||||
console.log(`Copied photo to existing album "${ALBUM_NAME}".`);
|
||||
}
|
||||
|
||||
return asset.uri;
|
||||
} catch (e: any) {
|
||||
console.error('Error saving asset to album, returning fallback asset URI:', e);
|
||||
console.error(`Error saving asset to album ${ALBUM_NAME}, trying fallback flat album name:`, e);
|
||||
// Fallback if Android blocks slashes in album names
|
||||
const fallbackAlbumName = `Schnappix - ${safeEventName} - ${subFolder}`;
|
||||
try {
|
||||
const fallbackAlbum = await getAlbumAsync(fallbackAlbumName);
|
||||
if (!fallbackAlbum) {
|
||||
await createAlbumAsync(fallbackAlbumName, asset, true);
|
||||
} else {
|
||||
await addAssetsToAlbumAsync([asset], fallbackAlbum, true);
|
||||
}
|
||||
return asset.uri;
|
||||
} catch {
|
||||
return asset.id;
|
||||
} catch (fallbackErr) {
|
||||
console.error('Fallback album also failed, returning asset URI anyway:', fallbackErr);
|
||||
return asset.uri;
|
||||
}
|
||||
} finally {
|
||||
// Cleanup our temp renamed file
|
||||
try {
|
||||
await FileSystem.deleteAsync(newLocalUri, { idempotent: true });
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user