Skip to content

Commit 73e85b4

Browse files
committed
Fix mission count attendance trigger and allow multi-image moments
1 parent 8ef66f3 commit 73e85b4

2 files changed

Lines changed: 52 additions & 18 deletions

File tree

pages/ProfilePage.tsx

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ const ProfileForm: React.FC<{ onSave: () => void }> = ({ onSave }) => {
195195
const MomentForm: React.FC<{ onSave: () => void }> = ({ onSave }) => {
196196
const { user, profile } = useAuth();
197197
const [caption, setCaption] = useState('');
198-
const [imagePreview, setImagePreview] = useState<string | null>(null);
198+
const [imagePreviews, setImagePreviews] = useState<string[]>([]);
199199
const [loading, setLoading] = useState(false);
200200

201201
// Helper function to get UUID from profile ID
@@ -257,21 +257,37 @@ const MomentForm: React.FC<{ onSave: () => void }> = ({ onSave }) => {
257257
};
258258

259259
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
260-
const file = e.target.files?.[0];
261-
if (file) {
262-
const reader = new FileReader();
263-
reader.onloadend = () => setImagePreview(reader.result as string);
264-
reader.readAsDataURL(file);
265-
}
260+
const files = Array.from(e.target.files || []);
261+
if (files.length === 0) return;
262+
263+
Promise.all(
264+
files.map((file) => new Promise<string>((resolve, reject) => {
265+
const reader = new FileReader();
266+
reader.onloadend = () => resolve(reader.result as string);
267+
reader.onerror = () => reject(new Error('Failed to read selected image'));
268+
reader.readAsDataURL(file);
269+
}))
270+
)
271+
.then((newPreviews) => setImagePreviews((prev) => [...prev, ...newPreviews]))
272+
.catch((error) => {
273+
console.error('Error reading images:', error);
274+
alert('Failed to read one or more selected images.');
275+
});
276+
277+
e.target.value = '';
266278
};
267279

268280
const handleSubmit = async (e: React.FormEvent) => {
269281
e.preventDefault();
270-
if (!imagePreview || !user) return;
282+
if (imagePreviews.length === 0 || !user) return;
271283
setLoading(true);
272284
try {
273285
const userId = await getUserIdAsUUID(user.id);
274-
await momentService.createMoment({ user_id: userId, image_url: imagePreview, caption });
286+
await Promise.all(
287+
imagePreviews.map((imageUrl) =>
288+
momentService.createMoment({ user_id: userId, image_url: imageUrl, caption })
289+
)
290+
);
275291
onSave();
276292
} catch (error: any) {
277293
console.error('Error creating moment:', error);
@@ -284,21 +300,31 @@ const MomentForm: React.FC<{ onSave: () => void }> = ({ onSave }) => {
284300
return (
285301
<form onSubmit={handleSubmit} className="space-y-6">
286302
<div className="flex flex-col items-center gap-4">
287-
{imagePreview ? (
288-
<div className="relative w-full aspect-square rounded-3xl overflow-hidden border-2 border-indigo-500/30">
289-
<img src={imagePreview} className="w-full h-full object-cover" alt="Preview" />
290-
<button onClick={() => setImagePreview(null)} className="absolute top-4 right-4 bg-black/50 p-2 rounded-full"><X size={20} /></button>
303+
{imagePreviews.length > 0 ? (
304+
<div className="w-full grid grid-cols-2 gap-3">
305+
{imagePreviews.map((preview, index) => (
306+
<div key={`${preview}-${index}`} className="relative aspect-square rounded-3xl overflow-hidden border-2 border-indigo-500/30">
307+
<img src={preview} className="w-full h-full object-cover" alt="Preview" />
308+
<button type="button" onClick={() => setImagePreviews((prev) => prev.filter((_, i) => i !== index))} className="absolute top-4 right-4 bg-black/50 p-2 rounded-full"><X size={20} /></button>
309+
</div>
310+
))}
291311
</div>
292312
) : (
293313
<label className="w-full aspect-square rounded-3xl border-2 border-dashed border-zinc-700 bg-zinc-900/50 flex flex-col items-center justify-center cursor-pointer hover:bg-zinc-800 transition-colors">
294314
<Camera size={48} className="text-zinc-500 mb-4" />
295-
<span className="text-[10px] font-black uppercase tracking-widest text-zinc-500">Capture the Moment</span>
296-
<input type="file" className="hidden" accept="image/*" onChange={handleFileChange} />
315+
<span className="text-[10px] font-black uppercase tracking-widest text-zinc-500">Capture Moment(s)</span>
316+
<input type="file" multiple className="hidden" accept="image/*" onChange={handleFileChange} />
317+
</label>
318+
)}
319+
{imagePreviews.length > 0 && (
320+
<label className="w-full py-3 rounded-2xl border border-zinc-700 bg-zinc-900/60 text-center text-xs font-black uppercase tracking-widest text-zinc-300 cursor-pointer hover:border-indigo-500/40 hover:text-indigo-300 transition-colors">
321+
Add More Images
322+
<input type="file" multiple className="hidden" accept="image/*" onChange={handleFileChange} />
297323
</label>
298324
)}
299325
</div>
300326
<Textarea label="CAPTION" value={caption} onChange={(e) => setCaption(e.target.value)} placeholder="What went down?" />
301-
<Button type="submit" disabled={loading || !imagePreview} className="w-full py-4">SHARE WITH SQUAD</Button>
327+
<Button type="submit" disabled={loading || imagePreviews.length === 0} className="w-full py-4">SHARE WITH SQUAD</Button>
302328
</form>
303329
);
304330
};

supabase_migration_extended.sql

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,20 @@ CREATE POLICY "System can create notifications" ON notifications FOR INSERT WITH
138138
CREATE OR REPLACE FUNCTION update_mission_count_on_attendance()
139139
RETURNS TRIGGER AS $$
140140
BEGIN
141-
-- Only increment if user marked as attended (true) and wasn't already attended
142-
IF NEW.attended = true AND (OLD.attended IS NULL OR OLD.attended = false) THEN
141+
-- INSERT: increment only when attendance is created as true
142+
IF TG_OP = 'INSERT' AND NEW.attended = true THEN
143+
UPDATE profiles
144+
SET mission_count = COALESCE(mission_count, 0) + 1
145+
WHERE id = NEW.user_id;
146+
END IF;
147+
148+
-- UPDATE: increment only when attendance changes from false -> true
149+
IF TG_OP = 'UPDATE' AND NEW.attended = true AND COALESCE(OLD.attended, false) = false THEN
143150
UPDATE profiles
144151
SET mission_count = COALESCE(mission_count, 0) + 1
145152
WHERE id = NEW.user_id;
146153
END IF;
154+
147155
RETURN NEW;
148156
END;
149157
$$ LANGUAGE plpgsql;

0 commit comments

Comments
 (0)