@crowdin/app-project-module 0.92.0 → 0.94.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.
package/out/types.d.ts CHANGED
@@ -261,7 +261,7 @@ export interface ClientConfig extends ImagePath {
261
261
  /**
262
262
  * property that tells backend that AiProvider and AiPromptProvider modules can cooperate only with each one
263
263
  */
264
- restrictAiToSameApp: boolean;
264
+ restrictAiToSameApp?: boolean;
265
265
  }
266
266
  export interface Environments {
267
267
  environments?: Environment | Environment[];
@@ -287,6 +287,7 @@
287
287
  {{#if dependencySettings}}
288
288
  data-dependency="{{dependencySettings}}"
289
289
  {{/if}}
290
+ onchange="settingsFieldChange(this)"
290
291
  >
291
292
  </crowdin-checkbox>
292
293
  {{/ifeq}}
@@ -312,6 +313,7 @@
312
313
  data-dependency="{{dependencySettings}}"
313
314
  {{/if}}
314
315
  is-position-fixed
316
+ onchange="settingsFieldChange(this)"
315
317
  >
316
318
  {{#each options}}
317
319
  <option
@@ -340,6 +342,7 @@
340
342
  data-dependency="{{dependencySettings}}"
341
343
  {{/if}}
342
344
  value="{{#if defaultValue}}{{defaultValue}}{{/if}}"
345
+ onchange="settingsFieldChange(this)"
343
346
  >
344
347
  </crowdin-input>
345
348
  {{/ifeq}}
@@ -357,7 +360,9 @@
357
360
  {{#if dependencySettings}}
358
361
  data-dependency="{{dependencySettings}}"
359
362
  {{/if}}
360
- value="{{#if defaultValue}}{{defaultValue}}{{/if}}">
363
+ value="{{#if defaultValue}}{{defaultValue}}{{/if}}"
364
+ onchange="settingsFieldChange(this)"
365
+ >
361
366
  </crowdin-textarea>
362
367
  {{/ifeq}}
363
368
  {{#ifeq type "notice"}}
@@ -457,7 +462,7 @@
457
462
  <script type="text/javascript">
458
463
  document.body.addEventListener('refreshFilesList', (e) => {
459
464
  if (e.detail.refreshIntegration) {
460
- getIntegrationData(true);
465
+ getIntegrationData({ hardReload: true });
461
466
  } else if (e.detail.refreshCrowdin) {
462
467
  getCrowdinData();
463
468
  }
@@ -468,7 +473,7 @@
468
473
  }
469
474
  {{#if integrationOneLevelFetching}}
470
475
  if (event.detail.componentId === 'integration-files' && event.detail.isOpen) {
471
- getIntegrationData(false, event.detail.id);
476
+ getIntegrationData({ parentId: event.detail.id, recursion: event.detail.recursion });
472
477
  }
473
478
  {{/if}}
474
479
  });
@@ -479,7 +484,7 @@
479
484
  });
480
485
  {{#if integrationSearchListener}}
481
486
  document.body.addEventListener("integrationFilterChange", (event) => {
482
- getIntegrationData(false, 0, event.detail);
487
+ getIntegrationData({ search: event.detail });
483
488
  })
484
489
  {{/if}}
485
490
  const appComponent = document.querySelector('crowdin-simple-integration');
@@ -516,7 +521,7 @@
516
521
  let fileToSync = [];
517
522
 
518
523
  getCrowdinData();
519
- getIntegrationData();
524
+ getIntegrationData({});
520
525
  getActiveJobs();
521
526
 
522
527
  function integrationLogout() {
@@ -583,12 +588,12 @@
583
588
  .finally(() => (appComponent.setAttribute('is-crowdin-loading', false)));
584
589
  }
585
590
 
586
- function getIntegrationData(hardReload = false, parentId = '', search = '', page = 0) {
591
+ function getIntegrationData({ hardReload = false, parentId = '', search = '', page = 0, recursion = false } = {}, recursionState = null) {
587
592
  appComponent.setAttribute('is-integration-loading', true);
588
- checkOrigin()
593
+ return checkOrigin()
589
594
  .then(restParams => fetch(`api/integration/data${restParams}&parent_id=${encodeURIComponent(parentId)}&search=${encodeURIComponent(search)}&page=${page}`))
590
595
  .then(checkResponse)
591
- .then((res) => {
596
+ .then(async (res) => {
592
597
  const files = res.data;
593
598
  const stopPagination = res.stopPagination;
594
599
  const tree = files.map(e => {
@@ -642,12 +647,61 @@
642
647
  const openIds = files.filter(e => !e.type).map(e => e.id);
643
648
  appComponent.setIntegrationOpenedFolders(openIds);
644
649
  }
650
+
651
+ if (recursion) {
652
+ if (!recursionState) {
653
+ recursionState = {
654
+ openedFolders: new Set(),
655
+ calls: 0,
656
+ setLoading(isLoading) {
657
+ appComponent.setAttribute('is-integration-loading', isLoading);
658
+ },
659
+ };
660
+ recursionState.setLoading(true);
661
+ }
662
+
663
+ if (parentId) {
664
+ recursionState.openedFolders.add(parentId);
665
+ }
666
+
667
+ const folderItems = files.filter(isFolder);
668
+
669
+ if (folderItems.length === 0) {
670
+ return Promise.resolve();
671
+ }
672
+
673
+ folderItems.forEach(folder => recursionState.openedFolders.add(folder.id));
674
+
675
+ const currentOpenedFolders = await appComponent.getIntegrationOpenedFolders();
676
+ appComponent.setIntegrationOpenedFolders([...currentOpenedFolders, ...recursionState.openedFolders]);
677
+
678
+ const recursivePromises = folderItems.map(async folder => {
679
+ recursionState.calls++;
680
+
681
+ try {
682
+ return getIntegrationData({ parentId: folder.id, recursion: true }, recursionState);
683
+ } finally {
684
+ recursionState.calls--;
685
+ if (recursionState.calls === 0) {
686
+ recursionState.setLoading(false);
687
+ }
688
+ }
689
+ });
690
+
691
+ return Promise.all(recursivePromises);
692
+ }
645
693
  })
646
694
  {{#or withCronSync webhooks}}
647
695
  .then(() => getSyncSettings('integration'))
648
696
  {{/or}}
649
697
  .catch(e => catchRejection(e, 'Can\'t fetch {{name}} templates'))
650
- .finally(() => (appComponent.setAttribute('is-integration-loading', false)));
698
+ .finally(() => {
699
+ if (!recursionState && !recursion) {
700
+ appComponent.setAttribute('is-integration-loading', false);
701
+ } else if (recursionState && recursionState.calls === 0) {
702
+ appComponent.setAttribute('is-integration-loading', false);
703
+ }
704
+ });
651
705
  }
652
706
 
653
707
  function getSyncSettings(provider) {
@@ -1315,6 +1369,7 @@
1315
1369
  });
1316
1370
  });
1317
1371
 
1372
+ let isValidationError = false;
1318
1373
  settingsSaveBtn.setAttribute('disabled', true);
1319
1374
  checkOrigin()
1320
1375
  .then(restParams => fetch('api/settings' + restParams, {
@@ -1327,15 +1382,33 @@
1327
1382
  showToast('Settings successfully saved');
1328
1383
  config = configReq;
1329
1384
  })
1330
- .catch(e => catchRejection(e, 'Can\'t save settings'))
1385
+ .catch(e => {
1386
+ if (e?.error && e?.error?.type === 'validation_error') {
1387
+ Object.keys(e?.error?.details).forEach(key => {
1388
+ const el = document.getElementById(`${key}-settings`);
1389
+ if (el) {
1390
+ el.setAttribute('error', 'true');
1391
+ el.setAttribute('error-text', e?.error?.details[key]);
1392
+ }
1393
+ });
1394
+
1395
+ isValidationError = true;
1396
+ showToast(e?.error?.details?.message || 'Can\'t save settings');
1397
+ settingsSaveBtn.removeAttribute('disabled');
1398
+ return;
1399
+ }
1400
+ catchRejection(e, 'Can\'t save settings')
1401
+ })
1331
1402
  .finally(() => {
1332
1403
  unsetLoader('#settings-modal');
1333
1404
  settingsSaveBtn.removeAttribute('disabled');
1334
- closeModal(settingsModal);
1335
- {{#if reloadOnConfigSave}}
1336
- getIntegrationData(true);
1337
- getCrowdinData();
1338
- {{/if}}
1405
+ if (!isValidationError) {
1406
+ closeModal(settingsModal);
1407
+ {{#if reloadOnConfigSave}}
1408
+ getIntegrationData({ hardReload: true });
1409
+ getCrowdinData();
1410
+ {{/if}}
1411
+ }
1339
1412
  });
1340
1413
  }
1341
1414
  {{else}}
@@ -1375,7 +1448,7 @@
1375
1448
 
1376
1449
  {{#if integrationPagination}}
1377
1450
  document.body.addEventListener('fileListEnd', (e) => {
1378
- getIntegrationData(false, 0, '', e.detail.page)
1451
+ getIntegrationData({ page: e.detail.page });
1379
1452
  })
1380
1453
  {{/if}}
1381
1454
 
@@ -1770,6 +1843,15 @@
1770
1843
  modal.style.display = 'none';
1771
1844
  modal.close()
1772
1845
  }
1846
+
1847
+ function settingsFieldChange(el) {
1848
+ el.removeAttribute('error');
1849
+ el.removeAttribute('error-text');
1850
+ }
1851
+
1852
+ function isFolder(e) {
1853
+ return !e.type && (e.nodeType === undefined || e.nodeType === folderType || e.nodeType === branchType);
1854
+ }
1773
1855
  </script>
1774
1856
 
1775
1857
  </html>
@@ -26,24 +26,28 @@
26
26
  crossorigin="anonymous"
27
27
  ></script>
28
28
  <script>
29
- Sentry.init({
30
- dsn: "{{sentryData.dsn}}",
31
- environment: "frontend",
32
- replaysSessionSampleRate: 0,
33
- replaysOnErrorSampleRate: 1.0,
34
- integrations: [new Sentry.Replay()],
35
- });
29
+ if (typeof Sentry !== 'undefined') {
30
+ Sentry.init({
31
+ dsn: "{{sentryData.dsn}}",
32
+ environment: "frontend",
33
+ replaysSessionSampleRate: 0,
34
+ replaysOnErrorSampleRate: 1.0,
35
+ integrations: [new Sentry.Replay()],
36
+ });
36
37
 
37
- Sentry.configureScope(function(scope) {
38
- scope.setTag("identifier", "{{sentryData.appIdentifier}}");
38
+ Sentry.configureScope(function(scope) {
39
+ scope.setTag("identifier", "{{sentryData.appIdentifier}}");
39
40
 
40
- AP.getContext(contextData => {
41
- const { user_id, ...rest } = contextData;
41
+ AP.getContext(contextData => {
42
+ const { user_id, ...rest } = contextData;
42
43
 
43
- user_id && scope.setUser({ id: user_id });
44
- scope.setTags(rest);
44
+ user_id && scope.setUser({ id: user_id });
45
+ scope.setTags(rest);
46
+ });
45
47
  });
46
- });
48
+ } else {
49
+ console.warn('Sentry is not available. This might be due to ad/tracking blockers or network issues.');
50
+ }
47
51
  </script>
48
52
  {{/if}}
49
53
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.92.0",
3
+ "version": "0.94.0",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",