@crowdin/app-project-module 0.10.2 → 0.11.1

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/README.md CHANGED
@@ -405,6 +405,7 @@ const configuration = {
405
405
  customFileFormat: {
406
406
  type: 'type-xyz',
407
407
  multilingual: false,
408
+ autoUploadTranslations: true, //useful when single language format
408
409
  signaturePatterns: {
409
410
  fileName: '^.+\.xml$'
410
411
  },
@@ -69,9 +69,19 @@ function handleParseFile(baseUrl, dataFolder, config, file, req) {
69
69
  }
70
70
  }
71
71
  if (res.strings) {
72
- const stringsJson = JSON.stringify(res.strings);
72
+ let strings = res.strings;
73
+ if (config.autoUploadTranslations) {
74
+ strings = strings.map(string => {
75
+ const translations = {};
76
+ req.targetLanguages
77
+ .map(targetLanguage => targetLanguage.id)
78
+ .forEach(targetLanguage => (translations[targetLanguage] = { text: string.text }));
79
+ return Object.assign(Object.assign({}, string), { translations });
80
+ });
81
+ }
82
+ const stringsJson = JSON.stringify(strings);
73
83
  if (Buffer.byteLength(stringsJson, 'utf8') < MAX_BODY_SIZE) {
74
- response.strings = res.strings;
84
+ response.strings = strings;
75
85
  }
76
86
  else {
77
87
  const storedFile = yield storeFile(stringsJson, dataFolder);
@@ -49,6 +49,8 @@ function handle(config, integration) {
49
49
  options.config = JSON.stringify(req.integrationSettings || {});
50
50
  }
51
51
  options.infoModal = integration.infoModal;
52
+ options.withCronSync = integration.withCronSync;
53
+ options.withWebhookSync = integration.withWebhookSync;
52
54
  return res.render(view, options);
53
55
  }));
54
56
  }
@@ -0,0 +1,3 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ export default function handle(): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const util_1 = require("../util");
13
+ const storage_1 = require("../storage");
14
+ function handle() {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ const { files, type, provider } = req.body;
17
+ const existingSettings = yield (0, storage_1.getSyncSettings)(req.crowdinContext.clientId, req.crowdinContext.crowdinId, type, provider);
18
+ if (existingSettings) {
19
+ yield (0, storage_1.updateSyncSettings)(JSON.stringify(files), req.crowdinContext.clientId, req.crowdinContext.crowdinId, type, provider);
20
+ }
21
+ else {
22
+ yield (0, storage_1.saveSyncSettings)(JSON.stringify(files), req.crowdinContext.clientId, req.crowdinContext.crowdinId, type, provider);
23
+ }
24
+ res.status(204).end();
25
+ }));
26
+ }
27
+ exports.default = handle;
@@ -0,0 +1,3 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ export default function handle(): (req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const util_1 = require("../util");
13
+ const storage_1 = require("../storage");
14
+ function handle() {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ let files = {};
17
+ const provider = req.params.provider;
18
+ const syncSettings = yield (0, storage_1.getSyncSettingsByProvider)(req.crowdinContext.clientId, provider);
19
+ if (syncSettings) {
20
+ files = JSON.parse(syncSettings.files) || [];
21
+ }
22
+ res.send(files);
23
+ }));
24
+ }
25
+ exports.default = handle;
package/out/index.js CHANGED
@@ -48,6 +48,8 @@ const integration_data_1 = __importDefault(require("./handlers/integration-data"
48
48
  const integration_login_1 = __importDefault(require("./handlers/integration-login"));
49
49
  const integration_logout_1 = __importDefault(require("./handlers/integration-logout"));
50
50
  const integration_update_1 = __importDefault(require("./handlers/integration-update"));
51
+ const sync_settings_save_1 = __importDefault(require("./handlers/sync-settings-save"));
52
+ const sync_settings_1 = __importDefault(require("./handlers/sync-settings"));
51
53
  const main_1 = __importDefault(require("./handlers/main"));
52
54
  const manifest_1 = __importDefault(require("./handlers/manifest"));
53
55
  const oauth_login_1 = __importDefault(require("./handlers/oauth-login"));
@@ -93,6 +95,8 @@ function addCrowdinEndpoints(app, config) {
93
95
  app.get('/api/integration/data', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, integration_data_1.default)(integrationLogic));
94
96
  app.post('/api/crowdin/update', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, crowdin_update_1.default)(config, integrationLogic));
95
97
  app.post('/api/integration/update', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, integration_update_1.default)(config, integrationLogic));
98
+ app.get('/api/sync-settings/:provider', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, sync_settings_1.default)());
99
+ app.post('/api/sync-settings', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, sync_settings_save_1.default)());
96
100
  if (integrationLogic.oauthLogin) {
97
101
  app.get((0, util_1.getOauthRoute)(integrationLogic), (0, oauth_login_1.default)(config, integrationLogic));
98
102
  }
@@ -101,6 +105,13 @@ function addCrowdinEndpoints(app, config) {
101
105
  cron.schedule(job.expression, () => (0, util_1.runJob)(config, integrationLogic, job).catch(console.error));
102
106
  });
103
107
  }
108
+ if (integrationLogic.withCronSync) {
109
+ cron.schedule('0 * * * *', () => (0, util_1.filesCron)(config, integrationLogic, '1').catch(console.error));
110
+ cron.schedule('0 */3 * * *', () => (0, util_1.filesCron)(config, integrationLogic, '3').catch(console.error));
111
+ cron.schedule('0 */6 * * *', () => (0, util_1.filesCron)(config, integrationLogic, '6').catch(console.error));
112
+ cron.schedule('0 */12 * * *', () => (0, util_1.filesCron)(config, integrationLogic, '12').catch(console.error));
113
+ cron.schedule('0 0 * * *', () => (0, util_1.filesCron)(config, integrationLogic, '24').catch(console.error));
114
+ }
104
115
  }
105
116
  if (config.customFileFormat) {
106
117
  app.post('/process', (0, process_1.default)(config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
@@ -127,6 +127,14 @@ export interface IntegrationLogic {
127
127
  * background jobs that will be executed for each crowdin project and user
128
128
  */
129
129
  cronJobs?: CronJob[];
130
+ withCronSync?: {
131
+ crowdin: boolean;
132
+ integration: boolean;
133
+ };
134
+ withWebhookSync?: {
135
+ crowdin: boolean;
136
+ integration: boolean;
137
+ };
130
138
  }
131
139
  export interface ConfigurationField {
132
140
  key: string;
@@ -321,9 +329,13 @@ export interface CustomFileFormatLogic {
321
329
  */
322
330
  multilingual?: boolean;
323
331
  /**
324
- * Contains fileName and/or fileContent regular expressions used to detect file type when uploading a new source file via UI (or via API without specified type parameter). If the file matches regular expressions, it's labeled as a custom format file.
332
+ * Contains fileName and/or fileContent regular expressions used to detect file type when uploading a new source file via UI (or via API without specified type parameter). If the file matches regular expressions, it's labeled as a custom format file.
325
333
  */
326
334
  signaturePatterns?: SignaturePatterns;
335
+ /**
336
+ * Flag to automatically upload translations
337
+ */
338
+ autoUploadTranslations?: boolean;
327
339
  /**
328
340
  * Used for initial source file upload, source file update, and translation upload
329
341
  */
@@ -371,7 +383,12 @@ export interface ProcessFileString {
371
383
  hasPlurals?: boolean;
372
384
  labels?: string[];
373
385
  text: string | SourceStringsModel.PluralText;
374
- translations?: any;
386
+ translations?: StringTranslations;
387
+ }
388
+ export interface StringTranslations {
389
+ [language: string]: {
390
+ text: string | SourceStringsModel.PluralText;
391
+ };
375
392
  }
376
393
  export interface CustomMTLogic {
377
394
  translate: (client: Crowdin, context: CrowdinContextInfo, projectId: number, source: string, target: string, strings: string[]) => Promise<string[]>;
@@ -411,3 +428,10 @@ export interface CrowdinAppUtilities {
411
428
  client?: Crowdin;
412
429
  }>;
413
430
  }
431
+ export interface IntegrationSyncSettings {
432
+ id: string;
433
+ files?: any;
434
+ integrationId: string;
435
+ crowdinId: string;
436
+ provider: string;
437
+ }
@@ -1,4 +1,4 @@
1
- import { CrowdinCredentials, IntegrationCredentials } from '../models';
1
+ import { CrowdinCredentials, IntegrationCredentials, IntegrationSyncSettings } from '../models';
2
2
  export declare function connect(folder: string): Promise<void>;
3
3
  export declare function saveCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
4
4
  export declare function updateCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
@@ -15,3 +15,8 @@ export declare function saveMetadata(id: string, metadata: any): Promise<void>;
15
15
  export declare function updateMetadata(id: string, metadata: any): Promise<void>;
16
16
  export declare function getMetadata(id: string): Promise<any | undefined>;
17
17
  export declare function deleteMetadata(id: string): Promise<void>;
18
+ export declare function getSyncSettingsByProvider(integrationId: string, provider: string): Promise<IntegrationSyncSettings | undefined>;
19
+ export declare function getAllSyncSettingsByType(type: string): Promise<IntegrationSyncSettings[]>;
20
+ export declare function saveSyncSettings(files: any, integrationId: string, crowdinId: string, type: string, provider: string): Promise<void>;
21
+ export declare function updateSyncSettings(files: any, integrationId: string, crowdinId: string, type: string, provider: string): Promise<void>;
22
+ export declare function getSyncSettings(integrationId: string, crowdinId: string, type: string, provider: string): Promise<IntegrationSyncSettings | undefined>;
@@ -13,7 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.deleteMetadata = exports.getMetadata = exports.updateMetadata = exports.saveMetadata = exports.deleteIntegrationCredentials = exports.getAllIntegrationCredentials = exports.getIntegrationCredentials = exports.updateIntegrationConfig = exports.updateIntegrationCredentials = exports.saveIntegrationCredentials = exports.deleteCrowdinCredentials = exports.getAllCrowdinCredentials = exports.getCrowdinCredentials = exports.updateCrowdinCredentials = exports.saveCrowdinCredentials = exports.connect = void 0;
16
+ exports.getSyncSettings = exports.updateSyncSettings = exports.saveSyncSettings = exports.getAllSyncSettingsByType = exports.getSyncSettingsByProvider = exports.deleteMetadata = exports.getMetadata = exports.updateMetadata = exports.saveMetadata = exports.deleteIntegrationCredentials = exports.getAllIntegrationCredentials = exports.getIntegrationCredentials = exports.updateIntegrationConfig = exports.updateIntegrationCredentials = exports.saveIntegrationCredentials = exports.deleteCrowdinCredentials = exports.getAllCrowdinCredentials = exports.getCrowdinCredentials = exports.updateCrowdinCredentials = exports.saveCrowdinCredentials = exports.connect = void 0;
17
17
  const path_1 = require("path");
18
18
  const sqlite3_1 = __importDefault(require("sqlite3"));
19
19
  let _res;
@@ -75,6 +75,17 @@ function connect(folder) {
75
75
  );
76
76
  `, []);
77
77
  yield _run(`
78
+ create table if not exists sync_settings
79
+ (
80
+ id integer not null primary key autoincrement,
81
+ files varchar null,
82
+ integration_id varchar not null,
83
+ crowdin_id varchar not null,
84
+ type varchar not null,
85
+ provider varchar not null
86
+ );
87
+ `, []);
88
+ yield _run(`
78
89
  create table if not exists app_metadata
79
90
  (
80
91
  id varchar not null primary key,
@@ -167,6 +178,7 @@ function deleteCrowdinCredentials(id) {
167
178
  return __awaiter(this, void 0, void 0, function* () {
168
179
  yield run('DELETE FROM crowdin_credentials where id = ?', [id]);
169
180
  yield run('DELETE FROM integration_credentials where crowdin_id = ?', [id]);
181
+ yield run('DELETE FROM sync_settings WHERE crowdin_id = ?', [id]);
170
182
  });
171
183
  }
172
184
  exports.deleteCrowdinCredentials = deleteCrowdinCredentials;
@@ -203,6 +215,7 @@ function getAllIntegrationCredentials(crowdinId) {
203
215
  exports.getAllIntegrationCredentials = getAllIntegrationCredentials;
204
216
  function deleteIntegrationCredentials(id) {
205
217
  return run('DELETE FROM integration_credentials where id = ?', [id]);
218
+ return run('DELETE FROM sync_settings where integration_id = ?', [id]);
206
219
  }
207
220
  exports.deleteIntegrationCredentials = deleteIntegrationCredentials;
208
221
  function saveMetadata(id, metadata) {
@@ -228,3 +241,41 @@ function deleteMetadata(id) {
228
241
  });
229
242
  }
230
243
  exports.deleteMetadata = deleteMetadata;
244
+ function getSyncSettingsByProvider(integrationId, provider) {
245
+ return __awaiter(this, void 0, void 0, function* () {
246
+ const row = yield get('SELECT id, files, integration_id as integrationId, crowdin_id as crowdinId, type, provider FROM sync_settings WHERE integration_id = ? AND provider = ?', [integrationId, provider]);
247
+ if (row) {
248
+ return row;
249
+ }
250
+ });
251
+ }
252
+ exports.getSyncSettingsByProvider = getSyncSettingsByProvider;
253
+ function getAllSyncSettingsByType(type) {
254
+ return __awaiter(this, void 0, void 0, function* () {
255
+ return each('SELECT id, files, integration_id as integrationId, crowdin_id as crowdinId, type, provider FROM sync_settings WHERE type = ?', [type]);
256
+ });
257
+ }
258
+ exports.getAllSyncSettingsByType = getAllSyncSettingsByType;
259
+ function saveSyncSettings(files, integrationId, crowdinId, type, provider) {
260
+ return run('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES (?, ?, ?, ?, ?)', [
261
+ files,
262
+ integrationId,
263
+ crowdinId,
264
+ type,
265
+ provider,
266
+ ]);
267
+ }
268
+ exports.saveSyncSettings = saveSyncSettings;
269
+ function updateSyncSettings(files, integrationId, crowdinId, type, provider) {
270
+ return run('UPDATE sync_settings SET files = ? WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [files, integrationId, crowdinId, type, provider]);
271
+ }
272
+ exports.updateSyncSettings = updateSyncSettings;
273
+ function getSyncSettings(integrationId, crowdinId, type, provider) {
274
+ return __awaiter(this, void 0, void 0, function* () {
275
+ const row = yield get('SELECT id, files, integration_id as integrationId, crowdin_id as crowdinId, type FROM sync_settings WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?', [integrationId, crowdinId, type, provider]);
276
+ if (row) {
277
+ return row;
278
+ }
279
+ });
280
+ }
281
+ exports.getSyncSettings = getSyncSettings;
@@ -14,3 +14,4 @@ export declare function applyDefaults(config: Config, integration: IntegrationLo
14
14
  export declare function prepareCrowdinClient(config: Config, credentials: CrowdinCredentials): Promise<Crowdin>;
15
15
  export declare function prepareIntegrationCredentials(config: Config, integration: IntegrationLogic, integrationCredentials: IntegrationCredentials): Promise<any>;
16
16
  export declare function runJob(config: Config, integration: IntegrationLogic, job: CronJob): Promise<void>;
17
+ export declare function filesCron(config: Config, integration: IntegrationLogic, period: string): Promise<void>;
package/out/util/index.js CHANGED
@@ -31,7 +31,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
31
31
  return (mod && mod.__esModule) ? mod : { "default": mod };
32
32
  };
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
- exports.runJob = exports.prepareIntegrationCredentials = exports.prepareCrowdinClient = exports.applyDefaults = exports.getRootFolder = exports.getOauthRoute = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
34
+ exports.filesCron = exports.runJob = exports.prepareIntegrationCredentials = exports.prepareCrowdinClient = exports.applyDefaults = exports.getRootFolder = exports.getOauthRoute = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
35
35
  const crowdin_api_client_1 = __importDefault(require("@crowdin/crowdin-api-client"));
36
36
  const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
37
37
  const axios_1 = __importDefault(require("axios"));
@@ -156,6 +156,46 @@ function applyDefaults(config, integration) {
156
156
  ],
157
157
  };
158
158
  }
159
+ if (integration.withCronSync) {
160
+ const getUserSettings = integration.getConfiguration;
161
+ integration.getConfiguration = (projectId, crowdinClient, integrationCredentials) => __awaiter(this, void 0, void 0, function* () {
162
+ let fields = [];
163
+ if (getUserSettings) {
164
+ fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
165
+ }
166
+ return [
167
+ {
168
+ key: 'schedule',
169
+ label: 'Sync schedule',
170
+ helpText: 'Set the frequency for pushing sources and translations',
171
+ type: 'select',
172
+ options: [
173
+ {
174
+ value: '1',
175
+ label: '1 hour',
176
+ },
177
+ {
178
+ value: '3',
179
+ label: '3 hours',
180
+ },
181
+ {
182
+ value: '6',
183
+ label: '6 hours',
184
+ },
185
+ {
186
+ value: '12',
187
+ label: '12 hours',
188
+ },
189
+ {
190
+ value: '24',
191
+ label: '24 hours',
192
+ },
193
+ ],
194
+ },
195
+ ...fields,
196
+ ];
197
+ });
198
+ }
159
199
  }
160
200
  exports.applyDefaults = applyDefaults;
161
201
  function prepareCrowdinClient(config, credentials) {
@@ -244,3 +284,31 @@ function runJob(config, integration, job) {
244
284
  });
245
285
  }
246
286
  exports.runJob = runJob;
287
+ function filesCron(config, integration, period) {
288
+ return __awaiter(this, void 0, void 0, function* () {
289
+ const syncSettingsList = yield (0, storage_1.getAllSyncSettingsByType)('schedule');
290
+ yield Promise.all(syncSettingsList.map((syncSettings) => __awaiter(this, void 0, void 0, function* () {
291
+ const files = JSON.parse(syncSettings.files);
292
+ const crowdinCredentials = yield (0, storage_1.getCrowdinCredentials)(syncSettings.crowdinId);
293
+ const integrationCredentials = yield (0, storage_1.getIntegrationCredentials)(syncSettings.integrationId);
294
+ if (crowdinCredentials && integrationCredentials) {
295
+ const intConfig = integrationCredentials.config
296
+ ? JSON.parse(integrationCredentials.config)
297
+ : { schedule: 24 };
298
+ if (period === intConfig.schedule) {
299
+ const projectId = crowdinAppFunctions.getProjectId(integrationCredentials.id);
300
+ const crowdinClient = yield prepareCrowdinClient(config, crowdinCredentials);
301
+ const apiCredentials = yield prepareIntegrationCredentials(config, integration, integrationCredentials);
302
+ const rootFolder = yield getRootFolder(config, integration, crowdinClient, projectId);
303
+ if (syncSettings.provider === 'crowdin') {
304
+ yield integration.updateIntegration(projectId, crowdinClient, apiCredentials, files, rootFolder, intConfig);
305
+ }
306
+ else {
307
+ yield integration.updateCrowdin(projectId, crowdinClient, apiCredentials, files, rootFolder, intConfig);
308
+ }
309
+ }
310
+ }
311
+ })));
312
+ });
313
+ }
314
+ exports.filesCron = filesCron;
@@ -61,6 +61,8 @@
61
61
  <crowdin-toasts></crowdin-toasts>
62
62
  </body>
63
63
  <script type="text/javascript">
64
+ const loginButton = document.querySelector('crowdin-button');
65
+
64
66
  function oauthLogin() {
65
67
  const url = '{{{ oauthUrl }}}';
66
68
  const oauthWindow = window.open(url, '{{ name }}', 'location=0,status=0,width=800,height=400');
@@ -86,6 +88,8 @@
86
88
  {{/ifeq}}
87
89
  {{/each}}
88
90
  };
91
+ loginButton.setAttribute('disabled', true);
92
+ loginButton.setAttribute('is-loading', true);
89
93
  checkOrigin()
90
94
  .then(queryParams =>
91
95
  fetch(`api/login${queryParams}`, {
@@ -96,7 +100,11 @@
96
100
  )
97
101
  .then(checkResponse)
98
102
  .then(reloadLocation)
99
- .catch(e => catchRejection(e, 'Credentials are not stored'));
103
+ .catch(e => catchRejection(e, 'Credentials are not stored'))
104
+ .finally(() => {
105
+ loginButton.setAttribute('disabled', false);
106
+ loginButton.setAttribute('is-loading', false);
107
+ });
100
108
  }
101
109
  </script>
102
110
 
@@ -13,7 +13,22 @@
13
13
  {{/if}}
14
14
  <crowdin-button icon-before="account_circle" onclick="integrationLogout()">Log out</crowdin-button>
15
15
  </div>
16
- <crowdin-simple-integration integration-name="{{name}}" integration-logo="logo.png">
16
+ <crowdin-simple-integration
17
+ {{#if withWebhookSync.crowdin}}
18
+ crowdin-sync="true"
19
+ {{/if}}
20
+ {{#if withWebhookSync.integration}}
21
+ integration-sync="true"
22
+ {{/if}}
23
+ {{#if withCronSync.crowdin}}
24
+ crowdin-schedule="true"
25
+ {{/if}}
26
+ {{#if withCronSync.integration}}
27
+ integration-schedule="true"
28
+ {{/if}}
29
+ integration-name="{{name}}"
30
+ integration-logo="logo.png"
31
+ >
17
32
  </crowdin-simple-integration>
18
33
  </div>
19
34
  <crowdin-toasts></crowdin-toasts>
@@ -25,7 +40,7 @@
25
40
  </crowdin-modal>
26
41
  {{/if}}
27
42
  {{#if configurationFields}}
28
- <crowdin-modal id="settings-modal" modal-width="50" modal-title="Settings" close-button-title="Close">
43
+ <crowdin-modal id="settings-modal" body-overflow-unset modal-width="50" modal-title="Settings" close-button-title="Close">
29
44
  <div id="modal-content">
30
45
  {{#each configurationFields}}
31
46
  {{#ifeq type "checkbox"}}
@@ -82,7 +97,6 @@
82
97
  {{/if}}
83
98
  </body>
84
99
  <script type="text/javascript">
85
-
86
100
  document.body.addEventListener('refreshFilesList', (e) => {
87
101
  if (e.detail.refreshIntegration) {
88
102
  getIntegrationData();
@@ -146,6 +160,9 @@
146
160
  .then((res) => {
147
161
  project = res;
148
162
  appComponent.setCrowdinLanguagesData(project.targetLanguages)
163
+ {{#if withCronSync}}
164
+ getSyncSettings('crowdin');
165
+ {{/if}}
149
166
  })
150
167
  .catch(e => catchRejection(e, 'Can\'t fetch Crowdin data'))
151
168
  .finally(() => (appComponent.setAttribute('is-crowdin-loading', false)));
@@ -173,10 +190,22 @@
173
190
  });
174
191
  appComponent.setIntegrationFilesData(tree);
175
192
  })
193
+ {{#if withCronSync}}
194
+ .then(() => getSyncSettings('integration'))
195
+ {{/if}}
176
196
  .catch(e => catchRejection(e, 'Can\'t fetch {{name}} templates'))
177
197
  .finally(() => (appComponent.setAttribute('is-integration-loading', false)));
178
198
  }
179
199
 
200
+ function getSyncSettings(provider) {
201
+ const filesComponent = appComponent.querySelector(`#${provider}-files`);
202
+ checkOrigin()
203
+ .then(restParams => fetch(`/api/sync-settings/${provider}` + restParams))
204
+ .then(checkResponse)
205
+ .then((res) => filesComponent.addScheduledFiles(res))
206
+ .catch(e => catchRejection(e, 'Can\'t fetch file progress'));
207
+ }
208
+
180
209
  function getFileProgress(fileId) {
181
210
  checkOrigin()
182
211
  .then(restParams => fetch(`api/crowdin/file-progress/${fileId}` + restParams))
@@ -311,6 +340,69 @@
311
340
  }
312
341
  }
313
342
  });
343
+
344
+ {{#if withCronSync}}
345
+ document.body.addEventListener('integrationScheduleSync', setIntegrationScheduleSync);
346
+ document.body.addEventListener('crowdinScheduleSync', setCrowdinScheduleSync);
347
+ document.body.addEventListener('crowdinDisableSync', disableCrowdinSync);
348
+ document.body.addEventListener('integrationDisableSync', disableIntegrationSync);
349
+
350
+ async function setIntegrationScheduleSync(e) {
351
+ if (e.detail.length === 0) {
352
+ showToast('Select templates which will be pushed to Crowdin');
353
+ return;
354
+ }
355
+ appComponent.setAttribute('is-integration-loading', true);
356
+ const syncedFiles = await appComponent.getIntegrationScheduleSync();
357
+
358
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
359
+ }
360
+
361
+ async function setCrowdinScheduleSync(e) {
362
+ if (e.detail.length === 0) {
363
+ showToast('Select templates which will be pushed to Crowdin');
364
+ return;
365
+ }
366
+ appComponent.setAttribute('is-crowdin-loading', true);
367
+ const syncedFiles = await appComponent.getCrowdinScheduleSync();
368
+
369
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
370
+ }
371
+
372
+ async function disableIntegrationSync(e) {
373
+ if (e.detail.length === 0) {
374
+ showToast('Select templates which will be pushed to Crowdin');
375
+ return;
376
+ }
377
+ appComponent.setAttribute('is-integration-loading', true);
378
+ const syncedFiles = await appComponent.getIntegrationScheduleSync();
379
+
380
+ updateSyncSettings(syncedFiles, 'schedule', 'integration');
381
+ }
382
+
383
+ async function disableCrowdinSync(e) {
384
+ if (e.detail.length === 0) {
385
+ showToast('Select templates which will be pushed to Crowdin');
386
+ return;
387
+ }
388
+ appComponent.setAttribute('is-crowdin-loading', true);
389
+ const syncedFiles = await appComponent.getCrowdinScheduleSync();
390
+
391
+ updateSyncSettings(syncedFiles, 'schedule', 'crowdin');
392
+ }
393
+
394
+ function updateSyncSettings(files, type, provider) {
395
+ checkOrigin()
396
+ .then(restParams => fetch('api/sync-settings' + restParams, {
397
+ method: 'POST',
398
+ headers: { 'Content-Type': 'application/json' },
399
+ body: JSON.stringify({ files, type, provider })
400
+ }))
401
+ .then(checkResponse)
402
+ .catch(e => catchRejection(e, 'Can\'t save schedule sync settings'))
403
+ .finally(() => (appComponent.setAttribute(`is-${provider}-loading`, false)));
404
+ }
405
+ {{/if}}
314
406
  </script>
315
407
 
316
408
  </html>
@@ -4,7 +4,10 @@
4
4
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
5
5
  <title></title>
6
6
  <link rel="stylesheet" href="assets/css/styles.css">
7
- <script src="https://crowdin-web-components.s3.amazonaws.com/crowdin-web-components.js"></script>
7
+ <script type="module"
8
+ src="https://crowdin-web-components.s3.amazonaws.com/crowdin-web-components/crowdin-web-components.esm.js"></script>
9
+ <script nomodule=""
10
+ src="https://crowdin-web-components.s3.amazonaws.com/crowdin-web-components/crowdin-web-components.js"></script>
8
11
  <script type="text/javascript" src="https://cdn.crowdin.com/apps/dist/iframe.js"></script>
9
12
  <script type="text/javascript" src="assets/js/polyfills/promise.js"></script>
10
13
  <script type="text/javascript" src="assets/js/polyfills/fetch.js"></script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.10.2",
3
+ "version": "0.11.1",
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",