mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-11-02 02:19:43 +00:00
Refactor profile screen and organize project structure
- Extract ProfileScreen and components to features/profile - Move shared hooks and stores to top-level - Add RLS policies for admin permissions - Add RELEASING.md guide - Update .gitignore
This commit is contained in:
1 parent
df4c4d486b
commit
8f8c6e401d
79 files changed
+6689
-4944
No files matched your search
@@ -0,0 +1,110 @@
|
||||
import { useState } from 'react';
|
||||
import { Alert, Platform } from 'react-native';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import { Video as VideoCompressor } from 'react-native-compressor';
|
||||
import { decode } from 'base64-arraybuffer';
|
||||
|
||||
import { supabase } from '../../../lib/supabase';
|
||||
import { Events } from '../../../lib/analytics';
|
||||
import { UploadStatus } from '../types';
|
||||
|
||||
export function useMediaUpload(matchId: string, userId: string | undefined) {
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [uploadStatus, setUploadStatus] = useState<UploadStatus>(null);
|
||||
|
||||
const uploadMedia = async (uri: string, mediaType: 'image' | 'video') => {
|
||||
if (!userId) return;
|
||||
setUploading(true);
|
||||
setUploadStatus({ mediaType, status: mediaType === 'video' ? 'compressing' : 'uploading', thumbnailUri: uri });
|
||||
|
||||
try {
|
||||
let fileUri = uri;
|
||||
let fileBody;
|
||||
let ext = mediaType === 'video' ? 'mp4' : 'jpg';
|
||||
|
||||
// Compress videos on native platforms before upload
|
||||
if (mediaType === 'video' && Platform.OS !== 'web') {
|
||||
try {
|
||||
console.log('Compressing video...');
|
||||
fileUri = await VideoCompressor.compress(uri, {
|
||||
compressionMethod: 'auto',
|
||||
maxSize: 720, // Max 720p resolution
|
||||
minimumFileSizeForCompress: 0, // Always compress
|
||||
});
|
||||
console.log('Video compressed successfully');
|
||||
ext = 'mp4'; // Compressed videos are always mp4
|
||||
// Update status to uploading after compression
|
||||
setUploadStatus({ mediaType, status: 'uploading', thumbnailUri: uri });
|
||||
} catch (compressError) {
|
||||
console.warn('Video compression failed, uploading original:', compressError);
|
||||
// Fall back to original if compression fails
|
||||
fileUri = uri;
|
||||
setUploadStatus({ mediaType, status: 'uploading', thumbnailUri: uri });
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.OS === 'web') {
|
||||
const response = await fetch(fileUri);
|
||||
const blob = await response.blob();
|
||||
fileBody = blob;
|
||||
|
||||
// Detect extension from blob type
|
||||
if (mediaType === 'video') {
|
||||
if (blob.type === 'video/mp4') ext = 'mp4';
|
||||
else if (blob.type === 'video/quicktime') ext = 'mov';
|
||||
else if (blob.type === 'video/webm') ext = 'webm';
|
||||
} else {
|
||||
if (blob.type === 'image/png') ext = 'png';
|
||||
else if (blob.type === 'image/jpeg' || blob.type === 'image/jpg') ext = 'jpg';
|
||||
else if (blob.type === 'image/gif') ext = 'gif';
|
||||
else if (blob.type === 'image/webp') ext = 'webp';
|
||||
}
|
||||
} else {
|
||||
const base64 = await FileSystem.readAsStringAsync(fileUri, { encoding: FileSystem.EncodingType.Base64 });
|
||||
fileBody = decode(base64);
|
||||
|
||||
if (mediaType !== 'video') {
|
||||
// Only use URI extension for non-videos (videos are always mp4 after compression)
|
||||
const uriExt = fileUri.split('.').pop();
|
||||
if (uriExt && uriExt !== fileUri) {
|
||||
ext = uriExt.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fileName = `${matchId}/${Date.now()}_${Math.random().toString(36).substring(7)}.${ext}`;
|
||||
const contentType = mediaType === 'video'
|
||||
? `video/${ext === 'mov' ? 'quicktime' : ext}`
|
||||
: `image/${ext === 'jpg' ? 'jpeg' : ext}`;
|
||||
|
||||
const { error: uploadError } = await supabase.storage
|
||||
.from("chat-media")
|
||||
.upload(fileName, fileBody, {
|
||||
contentType,
|
||||
upsert: false
|
||||
});
|
||||
|
||||
if (uploadError) throw uploadError;
|
||||
|
||||
// Store just the path, not the full URL (bucket is private)
|
||||
await supabase.from("messages").insert({
|
||||
match_id: matchId,
|
||||
user_id: userId,
|
||||
content: mediaType === 'video' ? 'Sent a video' : 'Sent an image',
|
||||
media_path: fileName,
|
||||
media_type: mediaType,
|
||||
media_expired: false,
|
||||
});
|
||||
Events.mediaUploaded();
|
||||
|
||||
} catch (error) {
|
||||
Alert.alert("Error", `Failed to upload ${mediaType}`);
|
||||
console.error(error);
|
||||
} finally {
|
||||
setUploading(false);
|
||||
setUploadStatus(null);
|
||||
}
|
||||
};
|
||||
|
||||
return { uploading, uploadStatus, uploadMedia };
|
||||
}
|
||||
Reference in new issue
Block a user