linguabot commited on
Commit
95ade26
·
verified ·
1 Parent(s): 3e3b2ab

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. client/src/pages/TutorialTasks.tsx +45 -19
client/src/pages/TutorialTasks.tsx CHANGED
@@ -1,4 +1,5 @@
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 {
@@ -670,10 +671,10 @@ const TutorialTasks: React.FC = () => {
670
  lockGridHeightById(taskId);
671
  withPreservedCardOffset(taskId, () => {
672
  setMutatingTaskId(taskId);
673
- setExpandedSections(prev => ({
674
- ...prev,
675
- [taskId]: !prev[taskId]
676
- }));
677
  });
678
  requestAnimationFrame(() => requestAnimationFrame(() => {
679
  unlockListHeight();
@@ -864,16 +865,39 @@ const TutorialTasks: React.FC = () => {
864
  lockCardHeightById(taskId);
865
  lockGridHeightById(taskId);
866
  withPreservedCardOffset(taskId, () => {
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,14 +906,14 @@ const TutorialTasks: React.FC = () => {
882
  const created = response.data;
883
  console.log('Submission created successfully:', created);
884
 
885
- // Optimistic append (disabled on Safari to prevent multi-phase jumps)
886
- if (!isSafari) {
887
- setUserSubmissions(prev => {
888
- const current = prev[taskId] || [];
889
- const next = [{ ...created, isOwner: true }, ...current];
890
- return { ...prev, [taskId]: next };
891
- });
892
- }
893
 
894
  // Defer state updates and minimal refetch
895
  // Measure grid height and set spacer before refetch to keep layout height constant
@@ -898,12 +922,14 @@ const TutorialTasks: React.FC = () => {
898
  if (gridHeight > 0) setSpacerHeights(prev => ({ ...prev, [taskId]: gridHeight }));
899
  withPreservedCardOffset(taskId, () => {
900
  React.startTransition(() => {
901
- setTranslationText({ ...translationText, [taskId]: '' });
902
- setSelectedGroups({ ...selectedGroups, [taskId]: 0 });
903
- // Narrow refetch: only this task's submissions
904
  api.get(`/api/submissions/by-source/${taskId}`).then(r => {
905
  const list = (r.data && r.data.submissions) || [];
906
- setUserSubmissions(prev => ({ ...prev, [taskId]: list }));
 
 
907
  requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
908
  }).catch(() => {
909
  requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
@@ -918,7 +944,7 @@ const TutorialTasks: React.FC = () => {
918
 
919
  } finally {
920
  withPreservedCardOffset(taskId, () => {
921
- setSubmitting({ ...submitting, [taskId]: false });
922
  });
923
  // release after a couple frames to let DOM settle (extra frame on Safari)
924
  const release = () => {
 
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 {
 
671
  lockGridHeightById(taskId);
672
  withPreservedCardOffset(taskId, () => {
673
  setMutatingTaskId(taskId);
674
+ setExpandedSections(prev => ({
675
+ ...prev,
676
+ [taskId]: !prev[taskId]
677
+ }));
678
  });
679
  requestAnimationFrame(() => requestAnimationFrame(() => {
680
  unlockListHeight();
 
865
  lockCardHeightById(taskId);
866
  lockGridHeightById(taskId);
867
  withPreservedCardOffset(taskId, () => {
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
  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
  // Measure grid height and set spacer before refetch to keep layout height constant
 
922
  if (gridHeight > 0) setSpacerHeights(prev => ({ ...prev, [taskId]: gridHeight }));
923
  withPreservedCardOffset(taskId, () => {
924
  React.startTransition(() => {
925
+ setTranslationText({ ...translationText, [taskId]: '' });
926
+ setSelectedGroups({ ...selectedGroups, [taskId]: 0 });
927
+ // Narrow refetch: only this task's submissions. Build off-DOM then swap in one commit
928
  api.get(`/api/submissions/by-source/${taskId}`).then(r => {
929
  const list = (r.data && r.data.submissions) || [];
930
+ flushSync(() => {
931
+ setUserSubmissions(prev => ({ ...prev, [taskId]: list }));
932
+ });
933
  requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
934
  }).catch(() => {
935
  requestAnimationFrame(() => setSpacerHeights(prev => ({ ...prev, [taskId]: 0 })));
 
944
 
945
  } finally {
946
  withPreservedCardOffset(taskId, () => {
947
+ setSubmitting({ ...submitting, [taskId]: false });
948
  });
949
  // release after a couple frames to let DOM settle (extra frame on Safari)
950
  const release = () => {