pxt-core 7.4.5 → 7.4.9

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/built/pxt.js CHANGED
@@ -97871,19 +97871,21 @@ var pxt;
97871
97871
  highContrast: false,
97872
97872
  language: pxt.appTarget.appTheme.defaultLocale,
97873
97873
  reader: "",
97874
- skillmap: { mapProgress: {}, completedTags: {} }
97874
+ skillmap: { mapProgress: {}, completedTags: {} },
97875
+ email: false
97875
97876
  });
97876
97877
  let _client;
97877
97878
  function client() { return _client; }
97878
97879
  auth.client = client;
97879
- const PREFERENCES_DEBOUNCE_MS = 5 * 1000;
97880
- const PREFERENCES_DEBOUNCE_MAX_MS = 30 * 1000;
97880
+ const PREFERENCES_DEBOUNCE_MS = 1 * 1000;
97881
+ const PREFERENCES_DEBOUNCE_MAX_MS = 10 * 1000;
97881
97882
  let debouncePreferencesChangedTimeout = 0;
97882
97883
  let debouncePreferencesChangedStarted = 0;
97883
97884
  class AuthClient {
97884
97885
  constructor() {
97885
97886
  this.initialUserPreferences_ = undefined;
97886
97887
  this.initialAuthCheck_ = undefined;
97888
+ this.prefPatchOps = [];
97887
97889
  pxt.Util.assert(!_client);
97888
97890
  // Set global instance.
97889
97891
  _client = this;
@@ -98073,23 +98075,41 @@ var pxt;
98073
98075
  }
98074
98076
  return result.success;
98075
98077
  }
98076
- async patchUserPreferencesAsync(ops) {
98078
+ async patchUserPreferencesAsync(ops, immediate = false) {
98077
98079
  ops = Array.isArray(ops) ? ops : [ops];
98080
+ ops = ops.filter(op => !!op);
98081
+ const curPref = await this.userPreferencesAsync();
98078
98082
  if (!ops.length) {
98079
- return;
98083
+ return { success: true, res: curPref };
98080
98084
  }
98081
- const curPref = await this.userPreferencesAsync();
98082
98085
  ts.pxtc.jsonPatch.patchInPlace(curPref, ops);
98083
98086
  await this.setUserPreferencesAsync(curPref);
98084
98087
  // If we're not logged in, non-persistent local state is all we'll use
98085
98088
  if (!await this.loggedInAsync()) {
98086
- return;
98089
+ return { success: true, res: curPref };
98087
98090
  }
98088
98091
  // If the user is logged in, save to cloud, but debounce the api call as this can be called frequently from skillmaps
98092
+ // Replace matching patches in the queue
98093
+ ops.forEach((incoming, iIncoming) => {
98094
+ this.prefPatchOps.some((existing, iExisting) => {
98095
+ if (!ts.pxtc.jsonPatch.opsAreEqual(existing, incoming))
98096
+ return false;
98097
+ // Patches are equivalent, replace in queue
98098
+ this.prefPatchOps[iExisting] = incoming;
98099
+ // Clear from incoming so we don't add it below
98100
+ ops[iIncoming] = null;
98101
+ return true;
98102
+ });
98103
+ });
98104
+ // Add remaining ops to the queue
98105
+ ops.filter(op => !!op).forEach(op => this.prefPatchOps.push(op));
98089
98106
  clearTimeout(debouncePreferencesChangedTimeout);
98090
98107
  const savePrefs = async () => {
98091
98108
  debouncePreferencesChangedStarted = 0;
98092
- const result = await this.apiAsync('/api/user/preferences', ops, 'PATCH');
98109
+ // Clear queued patch ops before send.
98110
+ const prefPatchOps = this.prefPatchOps;
98111
+ this.prefPatchOps = [];
98112
+ const result = await this.apiAsync('/api/user/preferences', prefPatchOps, 'PATCH');
98093
98113
  if (result.success) {
98094
98114
  pxt.debug("Updating local user preferences w/ cloud data after result of POST");
98095
98115
  // Set user profile from returned value so we stay in-sync
@@ -98098,15 +98118,22 @@ var pxt;
98098
98118
  else {
98099
98119
  pxt.reportError("identity", "update preferences failed", result);
98100
98120
  }
98121
+ return { success: result.success, res: result.resp };
98101
98122
  };
98102
- if (!debouncePreferencesChangedStarted) {
98103
- debouncePreferencesChangedStarted = pxt.U.now();
98104
- }
98105
- if (PREFERENCES_DEBOUNCE_MAX_MS < pxt.U.now() - debouncePreferencesChangedStarted) {
98106
- await savePrefs();
98123
+ if (immediate) {
98124
+ return await savePrefs();
98107
98125
  }
98108
98126
  else {
98109
- debouncePreferencesChangedTimeout = setTimeout(savePrefs, PREFERENCES_DEBOUNCE_MS);
98127
+ if (!debouncePreferencesChangedStarted) {
98128
+ debouncePreferencesChangedStarted = pxt.U.now();
98129
+ }
98130
+ if (PREFERENCES_DEBOUNCE_MAX_MS < pxt.U.now() - debouncePreferencesChangedStarted) {
98131
+ return await savePrefs();
98132
+ }
98133
+ else {
98134
+ debouncePreferencesChangedTimeout = setTimeout(savePrefs, PREFERENCES_DEBOUNCE_MS);
98135
+ return { success: false, res: undefined }; // This needs to be implemented correctly to return a promise with the debouncer
98136
+ }
98110
98137
  }
98111
98138
  }
98112
98139
  /*protected*/ hasUserId() {
@@ -98900,6 +98927,18 @@ var ts;
98900
98927
  return arr;
98901
98928
  }
98902
98929
  Util.reversed = reversed;
98930
+ function arrayEquals(a, b, compare = (c, d) => c === d) {
98931
+ if (a == b)
98932
+ return true;
98933
+ if (!a && b || !b && a || a.length !== b.length)
98934
+ return false;
98935
+ for (let i = 0; i < a.length; i++) {
98936
+ if (!compare(a[i], b[i]))
98937
+ return false;
98938
+ }
98939
+ return true;
98940
+ }
98941
+ Util.arrayEquals = arrayEquals;
98903
98942
  function iterMap(m, f) {
98904
98943
  Object.keys(m).forEach(k => f(k, m[k]));
98905
98944
  }
@@ -100723,6 +100762,10 @@ var ts;
100723
100762
  }
100724
100763
  }
100725
100764
  jsonPatch.patchInPlace = patchInPlace;
100765
+ function opsAreEqual(a, b) {
100766
+ return (a.op === b.op && pxtc.U.arrayEquals(a.path, b.path));
100767
+ }
100768
+ jsonPatch.opsAreEqual = opsAreEqual;
100726
100769
  })(jsonPatch = pxtc.jsonPatch || (pxtc.jsonPatch = {}));
100727
100770
  })(pxtc = ts.pxtc || (ts.pxtc = {}));
100728
100771
  })(ts || (ts = {}));
@@ -116191,8 +116234,8 @@ var pxt;
116191
116234
  if (a == b)
116192
116235
  return true;
116193
116236
  if (a.id !== b.id || a.type !== b.type ||
116194
- !arrayEquals(a.meta.tags, b.meta.tags) ||
116195
- !arrayEquals(a.meta.blockIDs, b.meta.blockIDs) ||
116237
+ !pxt.U.arrayEquals(a.meta.tags, b.meta.tags) ||
116238
+ !pxt.U.arrayEquals(a.meta.blockIDs, b.meta.blockIDs) ||
116196
116239
  a.meta.displayName !== b.meta.displayName)
116197
116240
  return false;
116198
116241
  switch (a.type) {
@@ -116201,7 +116244,7 @@ var pxt;
116201
116244
  return pxt.sprite.bitmapEquals(a.bitmap, b.bitmap);
116202
116245
  case "animation" /* Animation */:
116203
116246
  const bAnimation = b;
116204
- return a.interval === bAnimation.interval && arrayEquals(a.frames, bAnimation.frames, pxt.sprite.bitmapEquals);
116247
+ return a.interval === bAnimation.interval && pxt.U.arrayEquals(a.frames, bAnimation.frames, pxt.sprite.bitmapEquals);
116205
116248
  case "tilemap" /* Tilemap */:
116206
116249
  return a.data.equals(b.data);
116207
116250
  }
@@ -116323,17 +116366,6 @@ var pxt;
116323
116366
  }
116324
116367
  return id;
116325
116368
  }
116326
- function arrayEquals(a, b, compare = (c, d) => c === d) {
116327
- if (a == b)
116328
- return true;
116329
- if (!a && b || !b && a || a.length !== b.length)
116330
- return false;
116331
- for (let i = 0; i < a.length; i++) {
116332
- if (!compare(a[i], b[i]))
116333
- return false;
116334
- }
116335
- return true;
116336
- }
116337
116369
  function serializeTilemap(tilemap, id, name) {
116338
116370
  const tm = tilemap.tilemap.data();
116339
116371
  const data = new Uint8ClampedArray(5 + tm.data.length + tilemap.layers.data.length);
@@ -543,7 +543,7 @@ declare namespace pxt.editor {
543
543
  message?: string;
544
544
  data?: Map<string | number>;
545
545
  }
546
- type EditorMessageTutorialEventRequest = EditorMessageTutorialProgressEventRequest | EditorMessageTutorialCompletedEventRequest | EditorMessageTutorialLoadedEventRequest;
546
+ type EditorMessageTutorialEventRequest = EditorMessageTutorialProgressEventRequest | EditorMessageTutorialCompletedEventRequest | EditorMessageTutorialLoadedEventRequest | EditorMessageTutorialExitEventRequest;
547
547
  interface EditorMessageTutorialProgressEventRequest extends EditorMessageRequest {
548
548
  action: "tutorialevent";
549
549
  tutorialEvent: "progress";
package/built/pxtlib.d.ts CHANGED
@@ -57,6 +57,10 @@ declare namespace pxt.auth {
57
57
  type UserBadgeState = {
58
58
  badges: Badge[];
59
59
  };
60
+ type SetPrefResult = {
61
+ success: boolean;
62
+ res: UserPreferences;
63
+ };
60
64
  /**
61
65
  * User preference state that should be synced with the cloud.
62
66
  */
@@ -66,6 +70,7 @@ declare namespace pxt.auth {
66
70
  reader?: string;
67
71
  skillmap?: UserSkillmapState;
68
72
  badges?: UserBadgeState;
73
+ email?: boolean;
69
74
  };
70
75
  const DEFAULT_USER_PREFERENCES: () => UserPreferences;
71
76
  /**
@@ -124,7 +129,8 @@ declare namespace pxt.auth {
124
129
  username?: string;
125
130
  avatarUrl?: string;
126
131
  }): Promise<boolean>;
127
- patchUserPreferencesAsync(ops: ts.pxtc.jsonPatch.PatchOperation | ts.pxtc.jsonPatch.PatchOperation[]): Promise<void>;
132
+ private prefPatchOps;
133
+ patchUserPreferencesAsync(ops: ts.pxtc.jsonPatch.PatchOperation | ts.pxtc.jsonPatch.PatchOperation[], immediate?: boolean): Promise<SetPrefResult>;
128
134
  hasUserId(): boolean;
129
135
  private fetchUserAsync;
130
136
  private setUserProfileAsync;
@@ -269,6 +275,7 @@ declare namespace ts.pxtc.Util {
269
275
  export function listsEqual<T>(a: T[], b: T[]): boolean;
270
276
  export function oops(msg?: string): Error;
271
277
  export function reversed<T>(arr: T[]): T[];
278
+ export function arrayEquals<U>(a: U[], b: U[], compare?: (c: U, d: U) => boolean): boolean;
272
279
  export function iterMap<T>(m: pxt.Map<T>, f: (k: string, v: T) => void): void;
273
280
  export function mapMap<T, S>(m: pxt.Map<T>, f: (k: string, v: T) => S): pxt.Map<S>;
274
281
  export function values<T>(m: pxt.Map<T>): T[];
@@ -464,6 +471,7 @@ declare namespace ts.pxtc.jsonPatch {
464
471
  * Applies a set of JSON Patch operations to the object.
465
472
  */
466
473
  function patchInPlace(obj: any, ops: PatchOperation[]): void;
474
+ function opsAreEqual(a: PatchOperation, b: PatchOperation): boolean;
467
475
  }
468
476
  declare namespace ts.pxtc.jsonPatch.tests {
469
477
  function diffTests(): void;
package/built/pxtlib.js CHANGED
@@ -185,19 +185,21 @@ var pxt;
185
185
  highContrast: false,
186
186
  language: pxt.appTarget.appTheme.defaultLocale,
187
187
  reader: "",
188
- skillmap: { mapProgress: {}, completedTags: {} }
188
+ skillmap: { mapProgress: {}, completedTags: {} },
189
+ email: false
189
190
  });
190
191
  let _client;
191
192
  function client() { return _client; }
192
193
  auth.client = client;
193
- const PREFERENCES_DEBOUNCE_MS = 5 * 1000;
194
- const PREFERENCES_DEBOUNCE_MAX_MS = 30 * 1000;
194
+ const PREFERENCES_DEBOUNCE_MS = 1 * 1000;
195
+ const PREFERENCES_DEBOUNCE_MAX_MS = 10 * 1000;
195
196
  let debouncePreferencesChangedTimeout = 0;
196
197
  let debouncePreferencesChangedStarted = 0;
197
198
  class AuthClient {
198
199
  constructor() {
199
200
  this.initialUserPreferences_ = undefined;
200
201
  this.initialAuthCheck_ = undefined;
202
+ this.prefPatchOps = [];
201
203
  pxt.Util.assert(!_client);
202
204
  // Set global instance.
203
205
  _client = this;
@@ -387,23 +389,41 @@ var pxt;
387
389
  }
388
390
  return result.success;
389
391
  }
390
- async patchUserPreferencesAsync(ops) {
392
+ async patchUserPreferencesAsync(ops, immediate = false) {
391
393
  ops = Array.isArray(ops) ? ops : [ops];
394
+ ops = ops.filter(op => !!op);
395
+ const curPref = await this.userPreferencesAsync();
392
396
  if (!ops.length) {
393
- return;
397
+ return { success: true, res: curPref };
394
398
  }
395
- const curPref = await this.userPreferencesAsync();
396
399
  ts.pxtc.jsonPatch.patchInPlace(curPref, ops);
397
400
  await this.setUserPreferencesAsync(curPref);
398
401
  // If we're not logged in, non-persistent local state is all we'll use
399
402
  if (!await this.loggedInAsync()) {
400
- return;
403
+ return { success: true, res: curPref };
401
404
  }
402
405
  // If the user is logged in, save to cloud, but debounce the api call as this can be called frequently from skillmaps
406
+ // Replace matching patches in the queue
407
+ ops.forEach((incoming, iIncoming) => {
408
+ this.prefPatchOps.some((existing, iExisting) => {
409
+ if (!ts.pxtc.jsonPatch.opsAreEqual(existing, incoming))
410
+ return false;
411
+ // Patches are equivalent, replace in queue
412
+ this.prefPatchOps[iExisting] = incoming;
413
+ // Clear from incoming so we don't add it below
414
+ ops[iIncoming] = null;
415
+ return true;
416
+ });
417
+ });
418
+ // Add remaining ops to the queue
419
+ ops.filter(op => !!op).forEach(op => this.prefPatchOps.push(op));
403
420
  clearTimeout(debouncePreferencesChangedTimeout);
404
421
  const savePrefs = async () => {
405
422
  debouncePreferencesChangedStarted = 0;
406
- const result = await this.apiAsync('/api/user/preferences', ops, 'PATCH');
423
+ // Clear queued patch ops before send.
424
+ const prefPatchOps = this.prefPatchOps;
425
+ this.prefPatchOps = [];
426
+ const result = await this.apiAsync('/api/user/preferences', prefPatchOps, 'PATCH');
407
427
  if (result.success) {
408
428
  pxt.debug("Updating local user preferences w/ cloud data after result of POST");
409
429
  // Set user profile from returned value so we stay in-sync
@@ -412,15 +432,22 @@ var pxt;
412
432
  else {
413
433
  pxt.reportError("identity", "update preferences failed", result);
414
434
  }
435
+ return { success: result.success, res: result.resp };
415
436
  };
416
- if (!debouncePreferencesChangedStarted) {
417
- debouncePreferencesChangedStarted = pxt.U.now();
418
- }
419
- if (PREFERENCES_DEBOUNCE_MAX_MS < pxt.U.now() - debouncePreferencesChangedStarted) {
420
- await savePrefs();
437
+ if (immediate) {
438
+ return await savePrefs();
421
439
  }
422
440
  else {
423
- debouncePreferencesChangedTimeout = setTimeout(savePrefs, PREFERENCES_DEBOUNCE_MS);
441
+ if (!debouncePreferencesChangedStarted) {
442
+ debouncePreferencesChangedStarted = pxt.U.now();
443
+ }
444
+ if (PREFERENCES_DEBOUNCE_MAX_MS < pxt.U.now() - debouncePreferencesChangedStarted) {
445
+ return await savePrefs();
446
+ }
447
+ else {
448
+ debouncePreferencesChangedTimeout = setTimeout(savePrefs, PREFERENCES_DEBOUNCE_MS);
449
+ return { success: false, res: undefined }; // This needs to be implemented correctly to return a promise with the debouncer
450
+ }
424
451
  }
425
452
  }
426
453
  /*protected*/ hasUserId() {
@@ -1214,6 +1241,18 @@ var ts;
1214
1241
  return arr;
1215
1242
  }
1216
1243
  Util.reversed = reversed;
1244
+ function arrayEquals(a, b, compare = (c, d) => c === d) {
1245
+ if (a == b)
1246
+ return true;
1247
+ if (!a && b || !b && a || a.length !== b.length)
1248
+ return false;
1249
+ for (let i = 0; i < a.length; i++) {
1250
+ if (!compare(a[i], b[i]))
1251
+ return false;
1252
+ }
1253
+ return true;
1254
+ }
1255
+ Util.arrayEquals = arrayEquals;
1217
1256
  function iterMap(m, f) {
1218
1257
  Object.keys(m).forEach(k => f(k, m[k]));
1219
1258
  }
@@ -3037,6 +3076,10 @@ var ts;
3037
3076
  }
3038
3077
  }
3039
3078
  jsonPatch.patchInPlace = patchInPlace;
3079
+ function opsAreEqual(a, b) {
3080
+ return (a.op === b.op && pxtc.U.arrayEquals(a.path, b.path));
3081
+ }
3082
+ jsonPatch.opsAreEqual = opsAreEqual;
3040
3083
  })(jsonPatch = pxtc.jsonPatch || (pxtc.jsonPatch = {}));
3041
3084
  })(pxtc = ts.pxtc || (ts.pxtc = {}));
3042
3085
  })(ts || (ts = {}));
@@ -18505,8 +18548,8 @@ var pxt;
18505
18548
  if (a == b)
18506
18549
  return true;
18507
18550
  if (a.id !== b.id || a.type !== b.type ||
18508
- !arrayEquals(a.meta.tags, b.meta.tags) ||
18509
- !arrayEquals(a.meta.blockIDs, b.meta.blockIDs) ||
18551
+ !pxt.U.arrayEquals(a.meta.tags, b.meta.tags) ||
18552
+ !pxt.U.arrayEquals(a.meta.blockIDs, b.meta.blockIDs) ||
18510
18553
  a.meta.displayName !== b.meta.displayName)
18511
18554
  return false;
18512
18555
  switch (a.type) {
@@ -18515,7 +18558,7 @@ var pxt;
18515
18558
  return pxt.sprite.bitmapEquals(a.bitmap, b.bitmap);
18516
18559
  case "animation" /* Animation */:
18517
18560
  const bAnimation = b;
18518
- return a.interval === bAnimation.interval && arrayEquals(a.frames, bAnimation.frames, pxt.sprite.bitmapEquals);
18561
+ return a.interval === bAnimation.interval && pxt.U.arrayEquals(a.frames, bAnimation.frames, pxt.sprite.bitmapEquals);
18519
18562
  case "tilemap" /* Tilemap */:
18520
18563
  return a.data.equals(b.data);
18521
18564
  }
@@ -18637,17 +18680,6 @@ var pxt;
18637
18680
  }
18638
18681
  return id;
18639
18682
  }
18640
- function arrayEquals(a, b, compare = (c, d) => c === d) {
18641
- if (a == b)
18642
- return true;
18643
- if (!a && b || !b && a || a.length !== b.length)
18644
- return false;
18645
- for (let i = 0; i < a.length; i++) {
18646
- if (!compare(a[i], b[i]))
18647
- return false;
18648
- }
18649
- return true;
18650
- }
18651
18683
  function serializeTilemap(tilemap, id, name) {
18652
18684
  const tm = tilemap.tilemap.data();
18653
18685
  const data = new Uint8ClampedArray(5 + tm.data.length + tilemap.layers.data.length);