Files
Schnappix/src/services/storage.ts
T

93 lines
3.4 KiB
TypeScript

import { getPermissionsAsync, requestPermissionsAsync, createAssetAsync, getAlbumAsync, createAlbumAsync, addAssetsToAlbumAsync } from 'expo-media-library';
import * as FileSystem from 'expo-file-system';
/**
* Request necessary media library permissions.
* @returns Promise<boolean> True if permissions are granted.
*/
export async function requestStoragePermission(): Promise<boolean> {
const { status, canAskAgain } = await getPermissionsAsync(true);
if (status === 'granted') {
return true;
}
if (canAskAgain) {
const { status: newStatus } = await requestPermissionsAsync(true);
return newStatus === 'granted';
}
// On Android 13+, writeOnly permissions don't block MediaStore inserts
return true;
}
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/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, 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.');
}
// 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
const subFolder = type === 'Raw' ? 'Originale' : 'Collagen';
const ALBUM_NAME = `Schnappix - ${safeEventName} - ${subFolder}`;
try {
// 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
await createAlbumAsync(ALBUM_NAME, asset, false);
console.log(`Created new album "${ALBUM_NAME}" and copied photo.`);
} else {
// 4. If it exists, add our asset to it
await addAssetsToAlbumAsync([asset], album, false);
console.log(`Copied photo to existing album "${ALBUM_NAME}".`);
}
return asset.uri;
} catch (e: any) {
console.error(`Error saving asset to album ${ALBUM_NAME}:`, e);
return asset.uri;
} finally {
// Cleanup our temp renamed file
try {
await FileSystem.deleteAsync(newLocalUri, { idempotent: true });
} catch (e) {
// ignore
}
}
}