linguabot commited on
Commit
cb350e9
·
verified ·
1 Parent(s): b55b7b6

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. client/src/pages/TutorialTasks.tsx +21 -55
client/src/pages/TutorialTasks.tsx CHANGED
@@ -1,5 +1,4 @@
1
  import React, { useState, useEffect, useCallback, useRef, useLayoutEffect } from 'react';
2
- import { flushSync } from 'react-dom';
3
  import { useNavigate } from 'react-router-dom';
4
  import { api } from '../services/api';
5
  import {
@@ -868,36 +867,13 @@ const TutorialTasks: React.FC = () => {
868
  setSubmitting({ ...submitting, [taskId]: true });
869
  });
870
  const user = JSON.parse(localStorage.getItem('user') || '{}');
871
- // Safari: build the optimistic item synchronously so height stays stable; do network after paint
872
- let optimisticId: string | null = null;
873
- const userName = user.name || 'Unknown';
874
- flushSync(() => {
875
- const temp = {
876
- _id: `optimistic-${Date.now()}`,
877
- sourceTextId: taskId,
878
- transcreation: text,
879
- groupNumber: group,
880
- culturalAdaptations: [],
881
- username: userName,
882
- status: 'pending',
883
- isOwner: true,
884
- createdAt: new Date().toISOString()
885
- } as any;
886
- optimisticId = temp._id;
887
- setUserSubmissions(prev => {
888
- const current = prev[taskId] || [];
889
- const next = [temp, ...current];
890
- return { ...prev, [taskId]: next };
891
- });
892
- });
893
-
894
  const response = await new Promise((resolve) => requestAnimationFrame(async () => {
895
  const res = await api.post('/api/submissions', {
896
  sourceTextId: taskId,
897
  transcreation: text,
898
  groupNumber: group,
899
  culturalAdaptations: [],
900
- username: userName
901
  });
902
  resolve(res);
903
  })) as any;
@@ -906,44 +882,34 @@ const TutorialTasks: React.FC = () => {
906
  const created = response.data;
907
  console.log('Submission created successfully:', created);
908
 
909
- // Replace optimistic item (if any) with server item to avoid extra height changes
910
  setUserSubmissions(prev => {
911
  const current = prev[taskId] || [];
912
- const mapped = current.map(s => (optimisticId && s._id === optimisticId) ? { ...created, isOwner: true } : s);
913
- // If optimistic not found (e.g., not Safari), prepend as usual
914
- const next = (optimisticId && current.some(s => s._id === optimisticId)) ? mapped : [{ ...created, isOwner: true }, ...current];
915
  return { ...prev, [taskId]: next };
916
  });
917
 
918
  // Defer state updates and minimal refetch
919
- if (!isSafari) {
920
- // Measure grid height and set spacer before refetch to keep layout height constant
921
- const gridEl = submissionsGridRefs.current[taskId];
922
- const gridHeight = gridEl ? gridEl.getBoundingClientRect().height : 0;
923
- if (gridHeight > 0) setSpacerHeights(prev => ({ ...prev, [taskId]: gridHeight }));
924
- withPreservedCardOffset(taskId, () => {
925
- React.startTransition(() => {
926
- setTranslationText({ ...translationText, [taskId]: '' });
927
- setSelectedGroups({ ...selectedGroups, [taskId]: 0 });
928
- // Narrow refetch: only this task's submissions. Build off-DOM then swap in one commit
929
- api.get(`/api/submissions/by-source/${taskId}`).then(r => {
930
- const list = (r.data && r.data.submissions) || [];
931
- flushSync(() => {
932
- setUserSubmissions(prev => ({ ...prev, [taskId]: list }));
933
- });
934
- requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
935
- }).catch(() => {
936
- requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
937
- });
938
- });
939
- });
940
- } else {
941
- // Safari: skip refetch to avoid extra reflow; just clear local fields
942
  React.startTransition(() => {
943
  setTranslationText({ ...translationText, [taskId]: '' });
944
  setSelectedGroups({ ...selectedGroups, [taskId]: 0 });
 
 
 
 
 
 
 
 
 
945
  });
946
- }
947
  } else {
948
  console.error('Failed to submit translation:', response.data);
949
  }
@@ -2331,7 +2297,7 @@ const TutorialTasks: React.FC = () => {
2331
 
2332
  {/* All Submissions for this Task */}
2333
  {userSubmissions[task._id] && userSubmissions[task._id].length > 0 && (
2334
- <div ref={(el) => { submissionsContainerRefs.current[task._id] = el; }} className="bg-gradient-to-r from-white to-indigo-50 rounded-xl p-6 mb-6 border border-stone-200" style={{ overflowAnchor: 'none', overscrollBehavior: 'contain' as any }}>
2335
  <div className="flex items-center justify-between mb-4">
2336
  <div className="flex items-center space-x-2">
2337
  <div className="bg-indigo-100 rounded-full p-1">
@@ -2358,7 +2324,7 @@ const TutorialTasks: React.FC = () => {
2358
  expandedSections[task._id]
2359
  ? 'max-h-none overflow-visible'
2360
  : 'max-h-0 overflow-hidden'
2361
- }`} style={{ overscrollBehavior: 'contain' as any }}>
2362
  {userSubmissions[task._id].map((submission, index) => (
2363
  <div key={submission._id} className="bg-white rounded-lg p-3 border border-stone-200 flex flex-col justify-between h-full">
2364
  <div className="flex items-center justify-between mb-2">
 
1
  import React, { useState, useEffect, useCallback, useRef, useLayoutEffect } from 'react';
 
2
  import { useNavigate } from 'react-router-dom';
3
  import { api } from '../services/api';
4
  import {
 
867
  setSubmitting({ ...submitting, [taskId]: true });
868
  });
869
  const user = JSON.parse(localStorage.getItem('user') || '{}');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
870
  const response = await new Promise((resolve) => requestAnimationFrame(async () => {
871
  const res = await api.post('/api/submissions', {
872
  sourceTextId: taskId,
873
  transcreation: text,
874
  groupNumber: group,
875
  culturalAdaptations: [],
876
+ username: user.name || 'Unknown'
877
  });
878
  resolve(res);
879
  })) as any;
 
882
  const created = response.data;
883
  console.log('Submission created successfully:', created);
884
 
885
+ // Optimistic append to avoid large reflow from full refetch
886
  setUserSubmissions(prev => {
887
  const current = prev[taskId] || [];
888
+ // Put newest first like server returns
889
+ const next = [{ ...created, isOwner: true }, ...current];
 
890
  return { ...prev, [taskId]: next };
891
  });
892
 
893
  // Defer state updates and minimal refetch
894
+ // Measure grid height and set spacer before refetch to keep layout height constant
895
+ const gridEl = submissionsGridRefs.current[taskId];
896
+ const gridHeight = gridEl ? gridEl.getBoundingClientRect().height : 0;
897
+ if (gridHeight > 0) setSpacerHeights(prev => ({ ...prev, [taskId]: gridHeight }));
898
+ withPreservedCardOffset(taskId, () => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
899
  React.startTransition(() => {
900
  setTranslationText({ ...translationText, [taskId]: '' });
901
  setSelectedGroups({ ...selectedGroups, [taskId]: 0 });
902
+ // Narrow refetch: only this task's submissions
903
+ api.get(`/api/submissions/by-source/${taskId}`).then(r => {
904
+ const list = (r.data && r.data.submissions) || [];
905
+ setUserSubmissions(prev => ({ ...prev, [taskId]: list }));
906
+ // Release spacer one frame after list update to avoid jump
907
+ requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
908
+ }).catch(() => {
909
+ requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
910
+ });
911
  });
912
+ });
913
  } else {
914
  console.error('Failed to submit translation:', response.data);
915
  }
 
2297
 
2298
  {/* All Submissions for this Task */}
2299
  {userSubmissions[task._id] && userSubmissions[task._id].length > 0 && (
2300
+ <div ref={(el) => { submissionsContainerRefs.current[task._id] = el; }} className="bg-gradient-to-r from-white to-indigo-50 rounded-xl p-6 mb-6 border border-stone-200" style={{ overflowAnchor: 'none' }}>
2301
  <div className="flex items-center justify-between mb-4">
2302
  <div className="flex items-center space-x-2">
2303
  <div className="bg-indigo-100 rounded-full p-1">
 
2324
  expandedSections[task._id]
2325
  ? 'max-h-none overflow-visible'
2326
  : 'max-h-0 overflow-hidden'
2327
+ }`}>
2328
  {userSubmissions[task._id].map((submission, index) => (
2329
  <div key={submission._id} className="bg-white rounded-lg p-3 border border-stone-200 flex flex-col justify-between h-full">
2330
  <div className="flex items-center justify-between mb-2">