gt-sanity 0.0.5 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE.md +1 -8
  2. package/README.md +5 -5
  3. package/dist/index.d.mts +122 -95
  4. package/dist/index.d.ts +122 -95
  5. package/dist/index.js +9089 -1119
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +9099 -1100
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +11 -4
  10. package/src/adapter/core.ts +111 -9
  11. package/src/adapter/createTask.ts +1 -1
  12. package/src/adapter/getLocales.ts +2 -2
  13. package/src/adapter/types.ts +9 -0
  14. package/src/components/TranslationsProvider.tsx +942 -0
  15. package/src/components/page/BatchProgress.tsx +27 -0
  16. package/src/components/page/ImportAllDialog.tsx +51 -0
  17. package/src/components/page/ImportMissingDialog.tsx +55 -0
  18. package/src/components/page/TranslateAllDialog.tsx +55 -0
  19. package/src/components/page/TranslationsTable.tsx +81 -0
  20. package/src/components/page/TranslationsTool.tsx +338 -0
  21. package/src/components/shared/BaseTranslationWrapper.tsx +82 -0
  22. package/src/components/{LanguageStatus.tsx → shared/LanguageStatus.tsx} +2 -0
  23. package/src/components/shared/LocaleCheckbox.tsx +47 -0
  24. package/src/components/{ProgressBar.tsx → shared/ProgressBar.tsx} +2 -0
  25. package/src/components/shared/SingleDocumentView.tsx +108 -0
  26. package/src/components/tab/TranslationView.tsx +379 -0
  27. package/src/components/tab/TranslationsTab.tsx +25 -0
  28. package/src/configuration/baseDocumentLevelConfig/documentLevelPatch.ts +21 -11
  29. package/src/configuration/baseDocumentLevelConfig/helpers/createI18nDocAndPatchMetadata.ts +57 -23
  30. package/src/configuration/baseDocumentLevelConfig/helpers/createTranslationMetadata.ts +2 -0
  31. package/src/configuration/baseDocumentLevelConfig/helpers/getOrCreateTranslationMetadata.ts +2 -0
  32. package/src/configuration/baseDocumentLevelConfig/helpers/getTranslationMetadata.ts +2 -0
  33. package/src/configuration/baseDocumentLevelConfig/helpers/patchI18nDoc.ts +31 -8
  34. package/src/configuration/baseDocumentLevelConfig/index.ts +18 -101
  35. package/src/configuration/baseFieldLevelConfig.ts +19 -51
  36. package/src/configuration/utils/checkSerializationVersion.ts +2 -0
  37. package/src/configuration/utils/findDocumentAtRevision.ts +2 -0
  38. package/src/configuration/utils/findLatestDraft.ts +2 -0
  39. package/src/hooks/useClient.ts +3 -1
  40. package/src/hooks/useSecrets.ts +2 -0
  41. package/src/index.ts +91 -67
  42. package/src/sanity-api/findDocuments.ts +44 -0
  43. package/src/sanity-api/publishDocuments.ts +49 -0
  44. package/src/sanity-api/resolveRefs.ts +146 -0
  45. package/src/serialization/BaseDocumentMerger.ts +138 -0
  46. package/src/serialization/BaseSerializationConfig.ts +220 -0
  47. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/documentLevelDeserialization.test.ts.snap +189 -0
  48. package/src/serialization/__tests__/BaseDocumentDeserializer/__snapshots__/fieldLevelDeserialization.test.ts.snap +107 -0
  49. package/src/serialization/__tests__/BaseDocumentDeserializer/baseDeserialization.test.ts +397 -0
  50. package/src/serialization/__tests__/BaseDocumentDeserializer/documentLevelDeserialization.test.ts +107 -0
  51. package/src/serialization/__tests__/BaseDocumentDeserializer/fieldLevelDeserialization.test.ts +107 -0
  52. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/documentLevelMerge.test.ts.snap +193 -0
  53. package/src/serialization/__tests__/BaseDocumentMerger/__snapshots__/fieldLevelMerge.test.ts.snap +97 -0
  54. package/src/serialization/__tests__/BaseDocumentMerger/baseMerge.test.ts +36 -0
  55. package/src/serialization/__tests__/BaseDocumentMerger/documentLevelMerge.test.ts +96 -0
  56. package/src/serialization/__tests__/BaseDocumentMerger/fieldLevelMerge.test.ts +142 -0
  57. package/src/serialization/__tests__/BaseDocumentMerger/utils.ts +52 -0
  58. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentInlineMarks.test.ts.snap +39 -0
  59. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/documentLevelSerialization.test.ts.snap +8 -0
  60. package/src/serialization/__tests__/BaseDocumentSerializer/__snapshots__/fieldLevelSerialization.test.ts.snap +8 -0
  61. package/src/serialization/__tests__/BaseDocumentSerializer/baseSerialization.test.ts +345 -0
  62. package/src/serialization/__tests__/BaseDocumentSerializer/documentInlineMarks.test.ts +53 -0
  63. package/src/serialization/__tests__/BaseDocumentSerializer/documentLevelSerialization.test.ts +120 -0
  64. package/src/serialization/__tests__/BaseDocumentSerializer/fieldLevelSerialization.test.ts +153 -0
  65. package/src/serialization/__tests__/BaseDocumentSerializer/utils.ts +27 -0
  66. package/src/serialization/__tests__/README +2 -0
  67. package/src/serialization/__tests__/__fixtures__/annotationAndInlineBlocks.json +140 -0
  68. package/src/serialization/__tests__/__fixtures__/customStyles.json +62 -0
  69. package/src/serialization/__tests__/__fixtures__/documentInlineMarks.json +70 -0
  70. package/src/serialization/__tests__/__fixtures__/documentLevelArticle.json +185 -0
  71. package/src/serialization/__tests__/__fixtures__/fieldLevelArticle.json +107 -0
  72. package/src/serialization/__tests__/__fixtures__/inlineDocumentLevelArticle.json +134 -0
  73. package/src/serialization/__tests__/__fixtures__/inlineSchema.ts +270 -0
  74. package/src/serialization/__tests__/__fixtures__/messy-html.html +26 -0
  75. package/src/serialization/__tests__/__fixtures__/nestedLanguageFields.json +54 -0
  76. package/src/serialization/__tests__/__fixtures__/schema.ts +310 -0
  77. package/src/serialization/__tests__/global.setup.ts +40 -0
  78. package/src/serialization/__tests__/helpers.ts +132 -0
  79. package/src/serialization/data.ts +82 -0
  80. package/src/serialization/deserialize/BaseDocumentDeserializer.ts +171 -0
  81. package/src/serialization/deserialize/helpers.ts +42 -0
  82. package/src/serialization/helpers.ts +18 -0
  83. package/src/serialization/index.ts +11 -0
  84. package/src/serialization/serialize/fieldFilters.ts +124 -0
  85. package/src/serialization/serialize/index.ts +284 -0
  86. package/src/serialization/types.ts +41 -0
  87. package/src/translation/checkTranslationStatus.ts +42 -0
  88. package/src/translation/createJobs.ts +16 -0
  89. package/src/translation/downloadTranslations.ts +68 -0
  90. package/src/translation/importDocument.ts +23 -0
  91. package/src/translation/initProject.ts +61 -0
  92. package/src/translation/uploadFiles.ts +32 -0
  93. package/src/types.ts +7 -20
  94. package/src/utils/applyDocuments.ts +72 -0
  95. package/src/utils/batchProcessor.ts +111 -0
  96. package/src/utils/importUtils.ts +95 -0
  97. package/src/utils/serialize.ts +52 -0
  98. package/src/utils/shared.ts +1 -0
  99. package/src/adapter/index.ts +0 -13
  100. package/src/components/NewTask.tsx +0 -249
  101. package/src/components/TaskView.tsx +0 -255
  102. package/src/components/TranslationContext.tsx +0 -19
  103. package/src/components/TranslationView.tsx +0 -82
  104. package/src/components/TranslationsTab.tsx +0 -177
  105. package/src/configuration/baseDocumentLevelConfig/helpers/index.ts +0 -5
  106. package/src/configuration/baseDocumentLevelConfig/legacyDocumentLevelPatch.ts +0 -69
  107. package/src/configuration/index.ts +0 -18
  108. package/src/configuration/utils/index.ts +0 -3
@@ -1,249 +0,0 @@
1
- import React, {
2
- useState,
3
- useContext,
4
- ChangeEvent,
5
- useCallback,
6
- useEffect,
7
- } from 'react';
8
- import styled from 'styled-components';
9
- import {
10
- Button,
11
- Box,
12
- Flex,
13
- Grid,
14
- Select,
15
- Stack,
16
- Switch,
17
- Text,
18
- useToast,
19
- } from '@sanity/ui';
20
-
21
- import { TranslationContext } from './TranslationContext';
22
- import { TranslationLocale } from '../types';
23
-
24
- type Props = {
25
- locales: TranslationLocale[];
26
- refreshTask: () => Promise<void>;
27
- };
28
-
29
- type LocaleCheckboxProps = {
30
- locale: TranslationLocale;
31
- toggle: (locale: string, checked: boolean) => void;
32
- checked: boolean;
33
- };
34
-
35
- const WrapText = styled(Box)`
36
- white-space: normal;
37
- `;
38
-
39
- const LocaleCheckbox = ({ locale, toggle, checked }: LocaleCheckboxProps) => {
40
- const onClick = useCallback(
41
- () => toggle(locale.localeId, !checked),
42
- [locale, toggle, checked]
43
- );
44
-
45
- return (
46
- <Button
47
- mode='ghost'
48
- onClick={onClick}
49
- disabled={locale.enabled === false}
50
- style={{ cursor: `pointer` }}
51
- radius={2}
52
- >
53
- <Flex align='center' gap={3}>
54
- <Switch
55
- style={{ pointerEvents: `none` }}
56
- disabled={locale.enabled === false}
57
- onChange={onClick}
58
- checked={checked}
59
- />
60
- <WrapText>
61
- <Text size={1} weight='semibold'>
62
- {locale.description}
63
- </Text>
64
- </WrapText>
65
- </Flex>
66
- </Button>
67
- );
68
- };
69
-
70
- export const NewTask = ({ locales, refreshTask }: Props) => {
71
- const possibleLocales = locales.filter((locale) => locale.enabled !== false);
72
- // Lets just stick to the canonical document id for keeping track of
73
- // translations
74
- const [selectedLocales, setSelectedLocales] = useState<React.ReactNode[]>(
75
- locales
76
- .filter((locale) => locale.enabled !== false)
77
- .map((locale) => locale.localeId)
78
- );
79
-
80
- useEffect(() => {
81
- setSelectedLocales(
82
- locales
83
- .filter((locale) => locale.enabled !== false)
84
- .map((locale) => locale.localeId)
85
- );
86
- }, [locales]);
87
-
88
- const [selectedWorkflowUid, setSelectedWorkflowUid] = useState<string>();
89
- const [isBusy, setIsBusy] = useState(false);
90
-
91
- const context = useContext(TranslationContext);
92
- const toast = useToast();
93
-
94
- const toggleLocale = useCallback(
95
- (locale: string, selected: boolean) => {
96
- if (!selected) {
97
- setSelectedLocales(selectedLocales.filter((l) => l !== locale));
98
- } else if (!selectedLocales.includes(locale)) {
99
- setSelectedLocales([...selectedLocales, locale]);
100
- }
101
- },
102
- [selectedLocales, setSelectedLocales]
103
- );
104
-
105
- const createTask = useCallback(() => {
106
- if (!context) {
107
- toast.push({
108
- title: 'Unable to create task: missing context',
109
- status: 'error',
110
- closable: true,
111
- });
112
- return;
113
- }
114
-
115
- setIsBusy(true);
116
-
117
- context
118
- .exportForTranslation(context.documentInfo)
119
- .then((serialized) =>
120
- context.adapter.createTask(
121
- context.documentInfo,
122
- serialized,
123
- selectedLocales as string[],
124
- context.secrets,
125
- selectedWorkflowUid,
126
- context.callbackUrl
127
- )
128
- )
129
- .then(() => {
130
- toast.push({
131
- title: 'Job successfully created',
132
- status: 'success',
133
- closable: true,
134
- });
135
-
136
- /** Reset form fields */
137
- setSelectedLocales([]);
138
- setSelectedWorkflowUid('');
139
-
140
- /** Update task data in TranslationView */
141
- refreshTask();
142
- })
143
- .catch((err) => {
144
- let errorMsg;
145
- if (err instanceof Error) {
146
- errorMsg = err.message;
147
- } else {
148
- errorMsg = err ? String(err) : null;
149
- }
150
-
151
- toast.push({
152
- title: `Error creating translation job`,
153
- description: errorMsg,
154
- status: 'error',
155
- closable: true,
156
- });
157
- })
158
- .finally(() => {
159
- setIsBusy(false);
160
- });
161
- }, [context, selectedLocales, selectedWorkflowUid, toast, refreshTask]);
162
-
163
- const onClick = useCallback(() => {
164
- setSelectedLocales(
165
- possibleLocales.length === selectedLocales.length
166
- ? // Disable all
167
- []
168
- : // Enable all
169
- locales
170
- .filter((locale) => locale.enabled !== false)
171
- .map((locale) => locale.localeId)
172
- );
173
- }, [possibleLocales, selectedLocales, setSelectedLocales, locales]);
174
-
175
- const onToggle = useCallback(
176
- (locale: string, checked: boolean) => {
177
- toggleLocale(locale, checked);
178
- },
179
- [toggleLocale]
180
- );
181
-
182
- const onWorkflowChange = useCallback(
183
- (e: ChangeEvent<HTMLSelectElement>) => {
184
- setSelectedWorkflowUid(e.target.value);
185
- },
186
- [setSelectedWorkflowUid]
187
- );
188
-
189
- return (
190
- <Stack paddingTop={4} space={4}>
191
- <Text as='h2' weight='semibold' size={2}>
192
- Generate New Translations
193
- </Text>
194
- <Stack space={3}>
195
- <Flex align='center' justify='space-between'>
196
- <Text weight='semibold' size={1}>
197
- {possibleLocales.length === 1 ? `Select locale` : `Select locales`}
198
- </Text>
199
-
200
- <Button
201
- fontSize={1}
202
- padding={2}
203
- text='Toggle All'
204
- onClick={onClick}
205
- />
206
- </Flex>
207
-
208
- <Grid columns={[1, 1, 2, 3]} gap={1}>
209
- {(locales || []).map((l) => (
210
- <LocaleCheckbox
211
- key={l.localeId}
212
- locale={l}
213
- toggle={onToggle}
214
- checked={selectedLocales.includes(l.localeId)}
215
- />
216
- ))}
217
- </Grid>
218
- </Stack>
219
-
220
- {context?.workflowOptions && context.workflowOptions.length > 0 && (
221
- <Stack space={3}>
222
- <Text weight='semibold' size={1} as='label' htmlFor='workflow-select'>
223
- Select translation workflow
224
- </Text>
225
- <Grid columns={[1, 1, 2]}>
226
- <Select id='workflowSelect' onChange={onWorkflowChange}>
227
- <option>Default locale workflows</option>
228
- {context.workflowOptions.map((w) => (
229
- <option
230
- key={`workflow-opt-${w.workflowUid}`}
231
- value={w.workflowUid}
232
- >
233
- {w.workflowName}
234
- </option>
235
- ))}
236
- </Select>
237
- </Grid>
238
- </Stack>
239
- )}
240
-
241
- <Button
242
- onClick={createTask}
243
- disabled={isBusy || !selectedLocales.length}
244
- tone='positive'
245
- text={isBusy ? 'Queueing translations...' : 'Generate Translations'}
246
- />
247
- </Stack>
248
- );
249
- };
@@ -1,255 +0,0 @@
1
- import { useCallback, useContext, useState, useEffect } from 'react';
2
- import { Box, Button, Flex, Text, Stack, useToast, Switch } from '@sanity/ui';
3
- import {
4
- ArrowTopRightIcon,
5
- DownloadIcon,
6
- CheckmarkCircleIcon,
7
- } from '@sanity/icons';
8
-
9
- import { TranslationContext } from './TranslationContext';
10
- import { TranslationLocale, TranslationTask } from '../types';
11
- import { LanguageStatus } from './LanguageStatus';
12
-
13
- type JobProps = {
14
- task: TranslationTask;
15
- locales: TranslationLocale[];
16
- refreshTask: () => Promise<void>;
17
- };
18
-
19
- const getLocale = (
20
- localeId: string,
21
- locales: TranslationLocale[]
22
- ): TranslationLocale | undefined =>
23
- locales.find((l) => l.localeId === localeId);
24
-
25
- export const TaskView = ({ task, locales, refreshTask }: JobProps) => {
26
- const context = useContext(TranslationContext);
27
- const toast = useToast();
28
-
29
- const [isRefreshing, setIsRefreshing] = useState(false);
30
- const [autoRefresh, setAutoRefresh] = useState(true);
31
- const [isBusy, setIsBusy] = useState(false);
32
- const [autoImport, setAutoImport] = useState(true);
33
- const [importedFiles, setImportedFiles] = useState<Set<string>>(new Set());
34
-
35
- const importFile = useCallback(
36
- async (localeId: string) => {
37
- if (!context) {
38
- toast.push({
39
- title:
40
- 'Missing context, unable to import translation. Try refreshing or clicking away from this tab and back.',
41
- status: 'error',
42
- closable: true,
43
- });
44
- return;
45
- }
46
-
47
- const locale = getLocale(localeId, locales);
48
- const localeTitle = locale?.description || localeId;
49
-
50
- try {
51
- const translation = await context.adapter.getTranslation(
52
- task.document,
53
- localeId,
54
- context.secrets
55
- );
56
-
57
- const sanityId = context.localeIdAdapter
58
- ? await context.localeIdAdapter(localeId)
59
- : localeId;
60
-
61
- await context.importTranslation(sanityId, translation);
62
-
63
- setImportedFiles((prev) => new Set([...prev, localeId]));
64
-
65
- toast.push({
66
- title: `Imported ${localeTitle} translation`,
67
- status: 'success',
68
- closable: true,
69
- });
70
- } catch (err) {
71
- let errorMsg;
72
- if (err instanceof Error) {
73
- errorMsg = err.message;
74
- } else {
75
- errorMsg = err ? String(err) : null;
76
- }
77
-
78
- toast.push({
79
- title: `Error getting ${localeTitle} translation`,
80
- description: errorMsg,
81
- status: 'error',
82
- closable: true,
83
- });
84
- }
85
- },
86
- [locales, context, task.document, toast]
87
- );
88
-
89
- const checkAndImportCompletedFiles = useCallback(async () => {
90
- if (!autoImport || isBusy) return;
91
-
92
- const completedFiles = task.locales.filter(
93
- (locale) =>
94
- (locale.progress || 0) >= 100 && !importedFiles.has(locale.localeId)
95
- );
96
-
97
- if (completedFiles.length === 0) return;
98
-
99
- setIsBusy(true);
100
- try {
101
- for (const locale of completedFiles) {
102
- await importFile(locale.localeId);
103
- }
104
- } finally {
105
- setIsBusy(false);
106
- }
107
- }, [autoImport, isBusy, task.locales, importedFiles, importFile]);
108
-
109
- const handleRefreshClick = useCallback(async () => {
110
- if (isRefreshing) return;
111
- setIsRefreshing(true);
112
- await refreshTask();
113
- await checkAndImportCompletedFiles();
114
- setIsRefreshing(false);
115
- }, [refreshTask, setIsRefreshing, checkAndImportCompletedFiles]);
116
-
117
- const handleImportAll = useCallback(async () => {
118
- if (isBusy) return;
119
- setIsBusy(true);
120
-
121
- try {
122
- const filesToImport = task.locales.filter(
123
- (locale) => !importedFiles.has(locale.localeId)
124
- );
125
- for (const locale of filesToImport) {
126
- await importFile(locale.localeId);
127
- }
128
- } finally {
129
- setIsBusy(false);
130
- }
131
- }, [task.locales, importFile, isBusy, importedFiles]);
132
-
133
- useEffect(() => {
134
- if (!autoRefresh || importedFiles.size === task.locales.length) return;
135
-
136
- const interval = setInterval(async () => {
137
- await handleRefreshClick();
138
- }, 5000);
139
-
140
- return () => clearInterval(interval);
141
- }, [
142
- handleRefreshClick,
143
- autoRefresh,
144
- importedFiles.size,
145
- task.locales.length,
146
- ]);
147
-
148
- useEffect(() => {
149
- checkAndImportCompletedFiles();
150
- }, [checkAndImportCompletedFiles, task.locales]);
151
-
152
- useEffect(() => {
153
- setImportedFiles((prev) => {
154
- const newSet = new Set<string>();
155
- for (const localeId of prev) {
156
- if (task.locales.some((locale) => locale.localeId === localeId)) {
157
- newSet.add(localeId);
158
- }
159
- }
160
- return newSet;
161
- });
162
- }, [task.locales]);
163
-
164
- return (
165
- <Stack space={4}>
166
- <Flex align='center' justify='space-between'>
167
- <Text as='h2' weight='semibold' size={2}>
168
- Translation Progress
169
- </Text>
170
-
171
- <Flex gap={3} align='center'>
172
- <Flex gap={2} align='center'>
173
- <Text size={1}>Auto-refresh</Text>
174
- <Switch
175
- checked={autoRefresh}
176
- onChange={() => setAutoRefresh(!autoRefresh)}
177
- />
178
- </Flex>
179
- {task.linkToVendorTask && (
180
- <Button
181
- as='a'
182
- text='View Job'
183
- iconRight={ArrowTopRightIcon}
184
- href={task.linkToVendorTask}
185
- target='_blank'
186
- rel='noreferrer noopener'
187
- fontSize={1}
188
- padding={2}
189
- mode='bleed'
190
- />
191
- )}
192
- <Button
193
- fontSize={1}
194
- padding={2}
195
- text='Refresh Status'
196
- onClick={handleRefreshClick}
197
- disabled={isRefreshing}
198
- />
199
- </Flex>
200
- </Flex>
201
-
202
- <Box>
203
- {task.locales.map((localeTask) => {
204
- const reportPercent = localeTask.progress || 0;
205
- const locale = getLocale(localeTask.localeId, locales);
206
- return (
207
- <LanguageStatus
208
- key={[task.document.documentId, localeTask.localeId].join('.')}
209
- importFile={async () => {
210
- await importFile(localeTask.localeId);
211
- }}
212
- title={locale?.description || localeTask.localeId}
213
- progress={reportPercent}
214
- isImported={importedFiles.has(localeTask.localeId)}
215
- />
216
- );
217
- })}
218
- </Box>
219
- <Stack space={3}>
220
- <Flex gap={3} align='center' justify='space-between'>
221
- <Flex gap={2} align='center'>
222
- <Button
223
- mode='ghost'
224
- onClick={handleImportAll}
225
- text={isBusy ? 'Importing...' : 'Import All'}
226
- icon={isBusy ? null : DownloadIcon}
227
- disabled={isBusy || importedFiles.size === task.locales.length}
228
- />
229
- {importedFiles.size === task.locales.length &&
230
- task.locales.length > 0 && (
231
- <Flex gap={2} align='center' style={{ color: 'green' }}>
232
- <CheckmarkCircleIcon />
233
- <Text size={1}>All translations imported</Text>
234
- </Flex>
235
- )}
236
- {importedFiles.size > 0 &&
237
- importedFiles.size < task.locales.length && (
238
- <Text size={1} style={{ color: '#666' }}>
239
- {importedFiles.size}/{task.locales.length} imported
240
- </Text>
241
- )}
242
- </Flex>
243
- <Flex gap={2} align='center' style={{ whiteSpace: 'nowrap' }}>
244
- <Text size={1}>Auto-import when complete</Text>
245
- <Switch
246
- checked={autoImport}
247
- onChange={() => setAutoImport(!autoImport)}
248
- disabled={isBusy}
249
- />
250
- </Flex>
251
- </Flex>
252
- </Stack>
253
- </Stack>
254
- );
255
- };
@@ -1,19 +0,0 @@
1
- import React from 'react';
2
- import { GTSerializedDocument } from '../types';
3
- import { Adapter, GTFile, Secrets, WorkflowIdentifiers } from '../types';
4
-
5
- export type ContextProps = {
6
- documentInfo: GTFile;
7
- adapter: Adapter;
8
- importTranslation: (languageId: string, document: string) => Promise<void>;
9
- exportForTranslation: (documentInfo: GTFile) => Promise<GTSerializedDocument>;
10
- secrets: Secrets;
11
- workflowOptions?: WorkflowIdentifiers[];
12
- localeIdAdapter?: (id: string) => string | Promise<string>;
13
- callbackUrl?: string;
14
- mergeWithTargetLocale?: boolean;
15
- };
16
-
17
- export const TranslationContext = React.createContext<ContextProps | null>(
18
- null
19
- );
@@ -1,82 +0,0 @@
1
- /**
2
- * Add cleanup function to cancel async tasks
3
- */
4
-
5
- import { useCallback, useContext, useEffect, useState } from 'react';
6
- import { Stack, useToast } from '@sanity/ui';
7
- import { TranslationContext } from './TranslationContext';
8
-
9
- import { NewTask } from './NewTask';
10
- import { TaskView } from './TaskView';
11
- import { TranslationTask, TranslationLocale } from '../types';
12
-
13
- export const TranslationView = () => {
14
- const [locales, setLocales] = useState<TranslationLocale[]>([]);
15
- const [task, setTask] = useState<TranslationTask | null>(null);
16
-
17
- const context = useContext(TranslationContext);
18
- const toast = useToast();
19
-
20
- useEffect(() => {
21
- async function fetchData() {
22
- if (!context) {
23
- toast.push({
24
- title: 'Unable to load translation data: missing context',
25
- status: 'error',
26
- closable: true,
27
- });
28
- return;
29
- }
30
-
31
- const locales = await context.adapter.getLocales(context.secrets);
32
- setLocales(locales);
33
- try {
34
- const task = await context?.adapter.getTranslationTask(
35
- context.documentInfo,
36
- context.secrets
37
- );
38
- setTask(task);
39
- } catch (err) {
40
- let errorMsg;
41
- if (err instanceof Error) {
42
- errorMsg = err.message;
43
- } else {
44
- errorMsg = err ? String(err) : null;
45
- }
46
-
47
- // Hacky bypass for when a document is not yet translated and has never been uploaded
48
- if (errorMsg?.toLowerCase().includes('no source file found')) {
49
- return;
50
- }
51
-
52
- toast.push({
53
- title: `Error creating translation job`,
54
- description: errorMsg,
55
- status: 'error',
56
- closable: true,
57
- });
58
- }
59
- }
60
-
61
- fetchData();
62
- }, [context, toast]);
63
-
64
- const refreshTask = useCallback(async () => {
65
- const task = await context?.adapter.getTranslationTask(
66
- context.documentInfo,
67
- context.secrets
68
- );
69
- if (task) {
70
- setTask(task);
71
- }
72
- }, [context, setTask]);
73
-
74
- return (
75
- <Stack space={6}>
76
- <NewTask locales={locales} refreshTask={refreshTask} />
77
- {task && (
78
- <TaskView task={task} locales={locales} refreshTask={refreshTask} />
79
- )}
80
- </Stack>
81
- );
82
- };