Upload folder using huggingface_hub
Browse files
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 |
-
|
| 674 |
-
|
| 675 |
-
|
| 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 |
-
|
| 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:
|
| 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 |
-
//
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
| 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 |
-
|
| 902 |
-
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 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 = () => {
|