@shaxpir/duiduidui-models 1.0.1 → 1.2.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 (226) hide show
  1. package/dist/models/{content/ArrayView.d.ts → ArrayView.d.ts} +1 -1
  2. package/dist/models/{content/ArrayView.js → ArrayView.js} +14 -14
  3. package/dist/models/BayesianScore.d.ts +9 -0
  4. package/dist/models/BayesianScore.js +27 -0
  5. package/dist/models/{markup/ChangeModel.d.ts → ChangeModel.d.ts} +2 -3
  6. package/dist/models/{markup/ChangeModel.js → ChangeModel.js} +20 -10
  7. package/dist/models/{content/Content.d.ts → Content.d.ts} +9 -10
  8. package/dist/models/{content/Content.js → Content.js} +2 -6
  9. package/dist/models/{content/ContentKind.d.ts → ContentKind.d.ts} +3 -0
  10. package/dist/models/{content/ContentKind.js → ContentKind.js} +5 -1
  11. package/dist/models/{content/Device.d.ts → Device.d.ts} +15 -7
  12. package/dist/models/{content/Device.js → Device.js} +14 -18
  13. package/dist/models/Hanzi.d.ts +15 -0
  14. package/dist/models/{content/Manifest.d.ts → Manifest.d.ts} +7 -3
  15. package/dist/models/{content/Manifest.js → Manifest.js} +12 -8
  16. package/dist/models/{content/Media.d.ts → Media.d.ts} +2 -3
  17. package/dist/models/{content/Media.js → Media.js} +4 -5
  18. package/dist/models/{content/Metric.d.ts → Metric.d.ts} +2 -3
  19. package/dist/models/{content/Metric.js → Metric.js} +18 -20
  20. package/dist/models/{content/Model.d.ts → Model.d.ts} +3 -5
  21. package/dist/models/{content/Model.js → Model.js} +5 -6
  22. package/dist/models/{markup/Operation.d.ts → Operation.d.ts} +2 -5
  23. package/dist/models/{markup/Operation.js → Operation.js} +30 -25
  24. package/dist/models/Phrase.d.ts +39 -0
  25. package/dist/models/{content/Profile.d.ts → Profile.d.ts} +1 -2
  26. package/dist/models/{content/Profile.js → Profile.js} +7 -9
  27. package/dist/models/Progress.d.ts +26 -0
  28. package/dist/models/Progress.js +39 -0
  29. package/dist/models/Review.d.ts +14 -0
  30. package/dist/models/{content/Session.d.ts → Session.d.ts} +7 -15
  31. package/dist/models/Session.js +103 -0
  32. package/dist/models/Term.d.ts +36 -0
  33. package/dist/models/Term.js +82 -0
  34. package/dist/models/User.d.ts +32 -0
  35. package/dist/models/User.js +65 -0
  36. package/dist/models/Workspace.d.ts +37 -0
  37. package/dist/models/Workspace.js +94 -0
  38. package/dist/models/index.d.ts +22 -2
  39. package/dist/models/index.js +22 -2
  40. package/dist/repo/PermissiveJson1.d.ts +16 -16
  41. package/dist/repo/PermissiveJson1.js +17 -7
  42. package/dist/repo/ShareSync.d.ts +6 -6
  43. package/dist/repo/ShareSync.js +26 -19
  44. package/dist/repo/index.d.ts +1 -0
  45. package/dist/repo/index.js +1 -0
  46. package/dist/util/Encryption.d.ts +1 -1
  47. package/dist/util/index.d.ts +0 -6
  48. package/dist/util/index.js +0 -6
  49. package/lib/models/{content/ArrayView.ts → ArrayView.ts} +2 -2
  50. package/lib/models/BayesianScore.ts +32 -0
  51. package/lib/models/{markup/ChangeModel.ts → ChangeModel.ts} +3 -4
  52. package/lib/models/{content/Content.ts → Content.ts} +15 -18
  53. package/lib/models/{content/ContentKind.ts → ContentKind.ts} +6 -1
  54. package/lib/models/{content/Device.ts → Device.ts} +26 -22
  55. package/lib/models/Hanzi.ts +16 -0
  56. package/lib/models/{content/Manifest.ts → Manifest.ts} +15 -6
  57. package/lib/models/{content/Media.ts → Media.ts} +3 -4
  58. package/lib/models/Metric.ts +233 -0
  59. package/lib/models/{content/Model.ts → Model.ts} +4 -6
  60. package/lib/models/{markup/Operation.ts → Operation.ts} +6 -16
  61. package/lib/models/{content/Permissions.ts → Permissions.ts} +0 -2
  62. package/lib/models/Phrase.ts +53 -0
  63. package/lib/models/{content/Profile.ts → Profile.ts} +4 -6
  64. package/lib/models/Progress.ts +66 -0
  65. package/lib/models/Review.ts +18 -0
  66. package/lib/models/Session.ts +140 -0
  67. package/lib/models/Term.ts +139 -0
  68. package/lib/models/User.ts +97 -0
  69. package/lib/models/Workspace.ts +129 -0
  70. package/lib/models/index.ts +22 -2
  71. package/lib/repo/ShareSync.ts +9 -13
  72. package/lib/repo/index.ts +1 -0
  73. package/lib/util/Encryption.ts +1 -1
  74. package/lib/util/index.ts +0 -6
  75. package/package.json +4 -15
  76. package/dist/models/OutboundMessage.d.ts +0 -18
  77. package/dist/models/OutboundMessage.js +0 -25
  78. package/dist/models/content/Activity.d.ts +0 -10
  79. package/dist/models/content/Billing.d.ts +0 -144
  80. package/dist/models/content/Billing.js +0 -418
  81. package/dist/models/content/Book.d.ts +0 -77
  82. package/dist/models/content/Book.js +0 -407
  83. package/dist/models/content/Category.d.ts +0 -16
  84. package/dist/models/content/Category.js +0 -20
  85. package/dist/models/content/Checkpointable.d.ts +0 -21
  86. package/dist/models/content/Checkpointable.js +0 -156
  87. package/dist/models/content/Comment.d.ts +0 -19
  88. package/dist/models/content/Comment.js +0 -53
  89. package/dist/models/content/ConceptArt.d.ts +0 -31
  90. package/dist/models/content/ConceptArt.js +0 -84
  91. package/dist/models/content/Context.d.ts +0 -28
  92. package/dist/models/content/Context.js +0 -162
  93. package/dist/models/content/DevEnv.d.ts +0 -5
  94. package/dist/models/content/DevEnv.js +0 -9
  95. package/dist/models/content/Dictionary.d.ts +0 -31
  96. package/dist/models/content/Dictionary.js +0 -5
  97. package/dist/models/content/DictionaryEntry.d.ts +0 -20
  98. package/dist/models/content/DictionaryEntry.js +0 -2
  99. package/dist/models/content/ElasticModel.d.ts +0 -149
  100. package/dist/models/content/ElasticModel.js +0 -179
  101. package/dist/models/content/Environment.d.ts +0 -61
  102. package/dist/models/content/Environment.js +0 -124
  103. package/dist/models/content/ExportOptions.d.ts +0 -64
  104. package/dist/models/content/ExportOptions.js +0 -213
  105. package/dist/models/content/Folder.d.ts +0 -16
  106. package/dist/models/content/Folder.js +0 -33
  107. package/dist/models/content/Fragment.d.ts +0 -54
  108. package/dist/models/content/Fragment.js +0 -181
  109. package/dist/models/content/Hanzi.d.ts +0 -21
  110. package/dist/models/content/HighlightRule.d.ts +0 -9
  111. package/dist/models/content/HighlightRule.js +0 -2
  112. package/dist/models/content/Migration.d.ts +0 -68
  113. package/dist/models/content/Migration.js +0 -155
  114. package/dist/models/content/Phrase.d.ts +0 -8
  115. package/dist/models/content/Placeholder.d.ts +0 -8
  116. package/dist/models/content/Placeholder.js +0 -36
  117. package/dist/models/content/RichText.d.ts +0 -58
  118. package/dist/models/content/RichText.js +0 -79
  119. package/dist/models/content/Session.js +0 -173
  120. package/dist/models/content/Speech.d.ts +0 -67
  121. package/dist/models/content/Speech.js +0 -97
  122. package/dist/models/content/Stub.d.ts +0 -24
  123. package/dist/models/content/Stub.js +0 -179
  124. package/dist/models/content/Time.d.ts +0 -56
  125. package/dist/models/content/Time.js +0 -295
  126. package/dist/models/content/User.d.ts +0 -36
  127. package/dist/models/content/User.js +0 -95
  128. package/dist/models/content/Workspace.d.ts +0 -71
  129. package/dist/models/content/Workspace.js +0 -237
  130. package/dist/models/content/index.d.ts +0 -17
  131. package/dist/models/content/index.js +0 -34
  132. package/dist/models/legacy/LegacyBodyFormat.d.ts +0 -9
  133. package/dist/models/legacy/LegacyBodyFormat.js +0 -2
  134. package/dist/models/legacy/LegacyComment.d.ts +0 -12
  135. package/dist/models/legacy/LegacyComment.js +0 -2
  136. package/dist/models/legacy/LegacyContent.d.ts +0 -53
  137. package/dist/models/legacy/LegacyContent.js +0 -55
  138. package/dist/models/legacy/LegacyConversion.d.ts +0 -55
  139. package/dist/models/legacy/LegacyConversion.js +0 -401
  140. package/dist/models/legacy/LegacyFragment.d.ts +0 -21
  141. package/dist/models/legacy/LegacyFragment.js +0 -2
  142. package/dist/models/legacy/LegacyLocator.d.ts +0 -8
  143. package/dist/models/legacy/LegacyLocator.js +0 -31
  144. package/dist/models/legacy/LegacyOutboundMessage.d.ts +0 -16
  145. package/dist/models/legacy/LegacyOutboundMessage.js +0 -13
  146. package/dist/models/legacy/LegacyPicture.d.ts +0 -14
  147. package/dist/models/legacy/LegacyPicture.js +0 -2
  148. package/dist/models/legacy/LegacyProfile.d.ts +0 -9
  149. package/dist/models/legacy/LegacyProfile.js +0 -2
  150. package/dist/models/legacy/LegacySession.d.ts +0 -41
  151. package/dist/models/legacy/LegacySession.js +0 -35
  152. package/dist/models/legacy/LegacyStory.d.ts +0 -23
  153. package/dist/models/legacy/LegacyStory.js +0 -2
  154. package/dist/models/legacy/LegacyStub.d.ts +0 -15
  155. package/dist/models/legacy/LegacyStub.js +0 -2
  156. package/dist/models/legacy/LegacyTransaction.d.ts +0 -14
  157. package/dist/models/legacy/LegacyTransaction.js +0 -49
  158. package/dist/models/legacy/LegacyUser.d.ts +0 -28
  159. package/dist/models/legacy/LegacyUser.js +0 -32
  160. package/dist/models/legacy/LegacyWorkspace.d.ts +0 -23
  161. package/dist/models/legacy/LegacyWorkspace.js +0 -6
  162. package/dist/models/legacy/index.d.ts +0 -15
  163. package/dist/models/legacy/index.js +0 -32
  164. package/dist/models/markup/BodyFormat.d.ts +0 -14
  165. package/dist/models/markup/BodyFormat.js +0 -190
  166. package/dist/models/markup/DeltaOps.d.ts +0 -5
  167. package/dist/models/markup/DeltaOps.js +0 -74
  168. package/dist/models/markup/HtmlMarkup.d.ts +0 -4
  169. package/dist/models/markup/HtmlMarkup.js +0 -21
  170. package/dist/models/markup/index.d.ts +0 -4
  171. package/dist/models/markup/index.js +0 -21
  172. package/dist/util/Async.d.ts +0 -8
  173. package/dist/util/Async.js +0 -18
  174. package/dist/util/Base62.d.ts +0 -6
  175. package/dist/util/Base62.js +0 -47
  176. package/dist/util/BinarySearch.d.ts +0 -7
  177. package/dist/util/BinarySearch.js +0 -46
  178. package/dist/util/CachingHasher.d.ts +0 -8
  179. package/dist/util/CachingHasher.js +0 -41
  180. package/dist/util/Color.d.ts +0 -32
  181. package/dist/util/Color.js +0 -204
  182. package/dist/util/Dispatch.d.ts +0 -15
  183. package/dist/util/Dispatch.js +0 -79
  184. package/dist/util/EditDistance.d.ts +0 -13
  185. package/dist/util/EditDistance.js +0 -184
  186. package/dist/util/NumberFormat.d.ts +0 -14
  187. package/dist/util/NumberFormat.js +0 -224
  188. package/dist/util/Struct.d.ts +0 -4
  189. package/dist/util/Struct.js +0 -15
  190. package/dist/util/Template.d.ts +0 -16
  191. package/dist/util/Template.js +0 -128
  192. package/dist/util/Text.d.ts +0 -45
  193. package/dist/util/Text.js +0 -243
  194. package/dist/util/Tuples.d.ts +0 -9
  195. package/dist/util/Tuples.js +0 -135
  196. package/dist/util/Validate.d.ts +0 -4
  197. package/dist/util/Validate.js +0 -11
  198. package/dist/util/Vocabulary.d.ts +0 -3
  199. package/dist/util/Vocabulary.js +0 -35
  200. package/lib/models/content/Billing.ts +0 -558
  201. package/lib/models/content/DevEnv.ts +0 -5
  202. package/lib/models/content/DictionaryEntry.ts +0 -22
  203. package/lib/models/content/Hanzi.ts +0 -25
  204. package/lib/models/content/Phrase.ts +0 -10
  205. package/lib/models/content/Time.ts +0 -328
  206. package/lib/models/content/User.ts +0 -130
  207. package/lib/models/content/index.ts +0 -19
  208. package/lib/models/markup/DeltaOps.ts +0 -71
  209. package/lib/models/markup/index.ts +0 -6
  210. package/lib/util/Base62.ts +0 -47
  211. package/lib/util/CachingHasher.ts +0 -38
  212. package/lib/util/Dispatch.ts +0 -92
  213. package/lib/util/NumberFormat.ts +0 -194
  214. package/lib/util/Struct.ts +0 -14
  215. package/lib/util/Tuples.ts +0 -131
  216. /package/dist/models/{content/GeoLocation.d.ts → GeoLocation.d.ts} +0 -0
  217. /package/dist/models/{content/GeoLocation.js → GeoLocation.js} +0 -0
  218. /package/dist/models/{content/Hanzi.js → Hanzi.js} +0 -0
  219. /package/dist/models/{content/Permissions.d.ts → Permissions.d.ts} +0 -0
  220. /package/dist/models/{content/Permissions.js → Permissions.js} +0 -0
  221. /package/dist/models/{content/Phrase.js → Phrase.js} +0 -0
  222. /package/dist/models/{content/Activity.js → Review.js} +0 -0
  223. /package/dist/{models/markup → repo}/TextEditOps.d.ts +0 -0
  224. /package/dist/{models/markup → repo}/TextEditOps.js +0 -0
  225. /package/lib/models/{content/GeoLocation.ts → GeoLocation.ts} +0 -0
  226. /package/lib/{models/markup → repo}/TextEditOps.ts +0 -0
@@ -0,0 +1,66 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { CachingHasher, CompactDateTime, MultiClock } from "@shaxpir/shaxpir-common";
3
+ import { ShareSync, ShareSyncFactory } from '../repo';
4
+ import { BayesianScore } from './BayesianScore';
5
+ import { Content, ContentBody, ContentId, ContentMeta } from "./Content";
6
+ import { ContentKind } from './ContentKind';
7
+
8
+ export interface PhraseProgress extends BayesianScore {
9
+ text:string;
10
+ sense_rank:number;
11
+ learn_rank: number;
12
+ last_review_utc?: CompactDateTime;
13
+ }
14
+
15
+ // The Proficiency model represents our assessment of the user's overall language level (user_rank)
16
+ // as well as a map of phrases with their learn_rank, alpha, beta, and pr
17
+ export interface ProgressPayload {
18
+ user_rank:number;
19
+ phrases:Record<string, PhraseProgress>;
20
+ cognitive_load:number;
21
+ }
22
+
23
+ export interface ProgressBody extends ContentBody {
24
+ meta:ContentMeta;
25
+ payload:ProgressPayload;
26
+ }
27
+
28
+ export class Progress extends Content {
29
+
30
+ public static makeProgressId(userId:ContentId):ContentId {
31
+ return CachingHasher.makeMd5Base62Hash(userId + "-" + ContentKind.PROGRESS) as ContentId;
32
+ }
33
+
34
+ public static create(
35
+ userId:ContentId
36
+ ):Progress {
37
+ const now = MultiClock.now();
38
+ const progressId = Progress.makeProgressId(userId);
39
+ return ShareSyncFactory.get().createContent(
40
+ {
41
+ meta : {
42
+ ref : progressId,
43
+ kind : ContentKind.PROGRESS,
44
+ id : progressId,
45
+ owner : userId,
46
+ created_at : now,
47
+ updated_at : now
48
+ },
49
+ payload : {
50
+ user_rank : 0,
51
+ phrases : {},
52
+ cognitive_load : 0
53
+ }
54
+ }
55
+ ) as Progress;
56
+ }
57
+
58
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
59
+ super(doc, shouldAcquire, shareSync);
60
+ }
61
+
62
+ public get payload():ProgressPayload {
63
+ this.checkDisposed("Progress.payload");
64
+ return this.doc.data.payload as ProgressPayload;
65
+ }
66
+ }
@@ -0,0 +1,18 @@
1
+ import { MultiTime } from "@shaxpir/shaxpir-common";
2
+
3
+ export type ReviewResult = 'FAIL' | 'HARD' | 'GOOD' | 'EASY';
4
+
5
+ export interface ReviewLike {
6
+ result: ReviewResult;
7
+ at: MultiTime;
8
+ }
9
+
10
+ export interface Review extends ReviewLike {
11
+ text: string;
12
+ sense_rank: number;
13
+ }
14
+
15
+ export interface ImpliedReview extends ReviewLike {
16
+ context: string;
17
+ weight: number;
18
+ }
@@ -0,0 +1,140 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { CachingHasher, CompactDateTime, MultiClock, MultiTime, Time } from "@shaxpir/shaxpir-common";
3
+ import { ShareSync, ShareSyncFactory } from '../repo';
4
+ import { Content, ContentBody, ContentId, ContentMeta, ContentRef } from "./Content";
5
+ import { ContentKind } from './ContentKind';
6
+ import { Metric, MetricName } from './Metric';
7
+ import { BatchOperation } from './Operation';
8
+ import { Review } from './Review';
9
+ import { Workspace } from './Workspace';
10
+
11
+
12
+ export interface SessionPayload {
13
+ start:MultiTime;
14
+ end:MultiTime;
15
+ tz_offset:number;
16
+ device_id:ContentId;
17
+ duration_minutes:number;
18
+ reviews:Review[];
19
+ }
20
+
21
+ export interface SessionBody extends ContentBody {
22
+ meta:ContentMeta;
23
+ payload:SessionPayload;
24
+ }
25
+
26
+ export class Session extends Content {
27
+
28
+ public static makeSessionId(userId:ContentId):ContentId {
29
+ return CachingHasher.makeMd5Base62Hash(userId + "-" + ContentKind.SESSION) as ContentId;
30
+ }
31
+
32
+ public static makeSessionRef(userId:ContentId, at:MultiTime):ContentRef {
33
+ let sessionId = Session.makeSessionId(userId);
34
+ return `${sessionId}_${at.utc_time}` as ContentRef;
35
+ }
36
+
37
+ public static create(
38
+ userId:ContentId,
39
+ deviceId:ContentId
40
+ ):Session {
41
+
42
+ let now:MultiTime = MultiClock.now();
43
+ let sessionId = Session.makeSessionId(userId);
44
+ let sessionRef = Session.makeSessionRef(userId, now);
45
+
46
+ return ShareSyncFactory.get().createContent(
47
+ {
48
+ meta : {
49
+ ref : sessionRef,
50
+ kind : ContentKind.SESSION,
51
+ id : sessionId,
52
+ owner : userId,
53
+ created_at : now,
54
+ updated_at : now
55
+ },
56
+ payload : {
57
+ start : now,
58
+ end: now,
59
+ tz_offset : (new Date()).getTimezoneOffset(),
60
+ duration_minutes : 0,
61
+ device_id : deviceId,
62
+ reviews : []
63
+ }
64
+ }
65
+ ) as Session;
66
+ }
67
+
68
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
69
+ super(doc, shouldAcquire, shareSync);
70
+ }
71
+
72
+ public get payload():SessionPayload {
73
+ this.checkDisposed("Session.payload");
74
+ return this.doc.data.payload;
75
+ }
76
+
77
+ public touch():void {
78
+ this.checkDisposed("Session.touch");
79
+
80
+ const batch = new BatchOperation(this);
81
+
82
+ let nowUtc:CompactDateTime = batch.at.utc_time;
83
+ let nowLocal:CompactDateTime = batch.at.local_time;
84
+ let minutes:number = Time.absDiffMinutes(nowUtc, this.start.utc_time);
85
+
86
+ batch.setPathValue([ 'payload', 'duration_minutes' ] , minutes);
87
+ batch.setPathValue([ 'payload', 'end', 'utc_time' ] , nowUtc);
88
+ batch.setPathValue([ 'payload', 'end', 'local_time' ] , nowLocal);
89
+
90
+ batch.commit();
91
+ }
92
+
93
+ public get start():MultiTime {
94
+ this.checkDisposed("Session.start");
95
+ return this.payload.start;
96
+ }
97
+
98
+ public get end():MultiTime {
99
+ this.checkDisposed("Session.end");
100
+ return this.payload.end;
101
+ }
102
+
103
+ public get tzOffset():number {
104
+ this.checkDisposed("Session.tzOffset");
105
+ return this.payload.tz_offset;
106
+ }
107
+
108
+ public get durationMinutes():number {
109
+ this.checkDisposed("Session.durationMinutes");
110
+ return this.payload.duration_minutes;
111
+ }
112
+
113
+ public get deviceId():ContentId {
114
+ this.checkDisposed("Session.deviceId");
115
+ return this.payload.device_id;
116
+ }
117
+
118
+ public async modelUpdated():Promise<void> {
119
+ this.checkDisposed("Session.modelUpdated");
120
+ await super.modelUpdated();
121
+ const shareSync = this.shareSync;
122
+ const sessionId = this.id;
123
+ const userId = this.owner;
124
+ const date = Time.dateFrom(this.createdAt.local_time);
125
+ const workspaceId = Workspace.makeWorkspaceId(userId);
126
+ const workspace = await this.shareSync.acquire(ContentKind.WORKSPACE, workspaceId) as Workspace;
127
+ const sessionsOnDate:Session[] = await workspace.acquireSessionsFromDate(sessionId, date);
128
+ workspace.release();
129
+ let minutesWritingOnDate:number = 0;
130
+ for (let i = 0, len = sessionsOnDate.length; i < len; i++) {
131
+ const sessionOnDate = sessionsOnDate[i] as Session;
132
+ minutesWritingOnDate += sessionOnDate.durationMinutes;
133
+ sessionOnDate.release();
134
+ }
135
+ const minutesWritingMetricId = Metric.makeMetricId(userId, MetricName.MINUTES_WRITING);
136
+ const minutesWritingMetric = await shareSync.acquire(ContentKind.METRIC, minutesWritingMetricId) as Metric;
137
+ minutesWritingMetric.setDateAmount(date, minutesWritingOnDate);
138
+ minutesWritingMetric.release();
139
+ }
140
+ }
@@ -0,0 +1,139 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { CachingHasher, CompactDateTime, MultiClock } from "@shaxpir/shaxpir-common";
3
+ import { BayesianScore } from './BayesianScore';
4
+ import { Content, ContentBody, ContentId, ContentMeta } from "./Content";
5
+ import { BuiltInPhrase, Phrase, PhraseExample } from './Phrase';
6
+ import { ReviewLike } from "./Review";
7
+ import { ContentKind } from './ContentKind';
8
+ import { ShareSync, ShareSyncFactory } from '../repo';
9
+
10
+ export interface TermPayload extends BayesianScore {
11
+
12
+ // These fields are always populated. Note that for non-builtin entries, the sense_rank is always zero
13
+ text: string;
14
+ sense_rank: number;
15
+ learn_rank: number;
16
+ phrase_id: number | null;
17
+ starred_at: CompactDateTime | null;
18
+
19
+ // these phrase fields are omitted for builtin entries (e.g., when phrase_id is not null)
20
+ // TODO: what about server-provided entries with numeric 'phrase_id' (e.g., if we split the *huge* local
21
+ // db into a smaller local db with all beginner content, and a bigger cloud db with more advanced content)
22
+ hanzi_count?: number;
23
+ pinyin?: string;
24
+ pinyin_tokenized?: string;
25
+ transliteration?: string;
26
+ translation?: string;
27
+ notes?: string;
28
+ components?: PhraseExample[];
29
+ examples?: PhraseExample[];
30
+ keywords?: string[];
31
+
32
+ // if this is a builtin entry, these tags overlay the builtin tags
33
+ tags: string[];
34
+
35
+ reviews: ReviewLike[];
36
+ }
37
+
38
+ export interface TermBody extends ContentBody {
39
+ meta:ContentMeta;
40
+ payload:TermPayload;
41
+ }
42
+
43
+ export class Term extends Content {
44
+
45
+ public static makeTermId(userId:ContentId, text:string, senseRank:number):ContentId {
46
+ return CachingHasher.makeMd5Base62Hash(`${ContentKind.TERM}-${userId}-${text}-${senseRank}`) as ContentId;
47
+ }
48
+
49
+ public static forUserPhrase(
50
+ userId:ContentId,
51
+ phrase:Phrase
52
+ ):Term {
53
+ const now = MultiClock.now();
54
+ const termId = CachingHasher.makeMd5Base62Hash(`${userId}-${phrase.text}-${phrase.sense_rank}`) as ContentId;
55
+ const initalAlpha = phrase.learn_rank < 100 ? 1 : 2;
56
+ const initalBeta = phrase.learn_rank < 100 ? 1 : 2;
57
+ return ShareSyncFactory.get().createContent(
58
+ {
59
+ meta : {
60
+ ref : termId,
61
+ kind : ContentKind.TERM,
62
+ id : termId,
63
+ owner : userId,
64
+ created_at : now,
65
+ updated_at : now
66
+ },
67
+ payload : {
68
+
69
+ phrase_id : null,
70
+ text : phrase.text,
71
+ sense_rank : phrase.sense_rank,
72
+ hanzi_count : phrase.hanzi_count,
73
+ learn_rank : phrase.learn_rank,
74
+ pinyin : phrase.pinyin,
75
+ pinyin_tokenized : phrase.pinyin_tokenized,
76
+ transliteration : phrase.transliteration,
77
+ translation : phrase.translation,
78
+ notes : phrase.notes,
79
+
80
+ examples : phrase.examples,
81
+ keywords : phrase.keywords,
82
+ tags : phrase.tags,
83
+
84
+ starred_at : null,
85
+
86
+ alpha: 1,
87
+ beta: 1,
88
+ theta: 0.5,
89
+ reviews: []
90
+ }
91
+ }
92
+ ) as Term;
93
+ }
94
+
95
+ public static forBuiltinPhrase(
96
+ userId:ContentId,
97
+ phrase:BuiltInPhrase
98
+ ):Term {
99
+ const now = MultiClock.now();
100
+ const termId = CachingHasher.makeMd5Base62Hash(`${userId}-${phrase.text}-${phrase.sense_rank}`) as ContentId;
101
+ return ShareSyncFactory.get().createContent(
102
+ {
103
+ meta : {
104
+ ref : termId,
105
+ kind : ContentKind.TERM,
106
+ id : termId,
107
+ owner : userId,
108
+ created_at : now,
109
+ updated_at : now
110
+ },
111
+ payload : {
112
+
113
+ text: phrase.text,
114
+ sense_rank: phrase.sense_rank,
115
+ learn_rank: phrase.learn_rank,
116
+ phrase_id : phrase.id,
117
+ starred_at: null,
118
+
119
+ tags : [],
120
+
121
+ alpha: 1,
122
+ beta: 1,
123
+ theta: 0.5,
124
+
125
+ reviews: []
126
+ }
127
+ }
128
+ ) as Term;
129
+ }
130
+
131
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
132
+ super(doc, shouldAcquire, shareSync);
133
+ }
134
+
135
+ public get payload():TermPayload {
136
+ this.checkDisposed("Term.payload");
137
+ return this.doc.data.payload as TermPayload;
138
+ }
139
+ }
@@ -0,0 +1,97 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { CompactDateTime, MultiClock, MultiTime } from '@shaxpir/shaxpir-common';
3
+ import { ArrayView } from './ArrayView';
4
+ import { Content, ContentId, ContentMeta } from "./Content";
5
+ import { ContentKind } from './ContentKind';
6
+ import { BatchOperation } from './Operation';
7
+ import { ShareSync, ShareSyncFactory } from '../repo';
8
+
9
+ export interface VerifiablePhone {
10
+ phone:string;
11
+ verified_at:CompactDateTime;
12
+ }
13
+
14
+ export interface VerifiableEmail {
15
+ email:string;
16
+ verified_at:CompactDateTime;
17
+ }
18
+
19
+ export interface UserPayload {
20
+ emails:VerifiableEmail[];
21
+ phones:VerifiablePhone[];
22
+ }
23
+
24
+ export interface UserBody {
25
+ meta:ContentMeta;
26
+ payload:UserPayload;
27
+ }
28
+
29
+ export class User extends Content {
30
+
31
+ private _emails:ArrayView<VerifiableEmail>;
32
+ private _phones:ArrayView<VerifiablePhone>;
33
+
34
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
35
+ super(doc, shouldAcquire, shareSync);
36
+ this._emails = new ArrayView<VerifiableEmail>(this, [ 'payload', 'emails' ]);
37
+ this._phones = new ArrayView<VerifiablePhone>(this, [ 'payload', 'phones' ]);
38
+ }
39
+
40
+ public get payload():UserPayload {
41
+ return this.doc.data.payload as UserPayload;
42
+ }
43
+
44
+ public static create(
45
+ userId:ContentId,
46
+ payload:UserPayload,
47
+ createdAt?:MultiTime
48
+ ):User {
49
+ // User creation always happens in UTC time
50
+ const now = MultiClock.utcNow();
51
+ // If the createdAt time is not provided, use the current time
52
+ createdAt ??= now;
53
+ return ShareSyncFactory.get().createContent(
54
+ {
55
+ meta : {
56
+ ref : userId,
57
+ kind : ContentKind.USER,
58
+ id : userId,
59
+ owner : userId,
60
+ created_at : createdAt,
61
+ updated_at : now
62
+ },
63
+ payload : payload
64
+ }
65
+ ) as User;
66
+ }
67
+
68
+ public get emails():ArrayView<VerifiableEmail> {
69
+ return this._emails;
70
+ }
71
+
72
+ public get phones():ArrayView<VerifiablePhone> {
73
+ return this._phones;
74
+ }
75
+
76
+ public verifyEmail(email:string, time:CompactDateTime):void {
77
+ const idx = this._emails.firstIndexWhere(e => e.email === email);
78
+ if (idx >= 0 && this._emails.get(idx).verified_at !== time) {
79
+ const batch = new BatchOperation(this);
80
+ batch.setPathValue([ 'payload', 'emails', idx, 'verified_at' ], time);
81
+ batch.commit();
82
+ } else if (idx < 0) {
83
+ this._emails.push({ email, verified_at: time });
84
+ }
85
+ }
86
+
87
+ public verifyPhone(phone:string, time:CompactDateTime):void {
88
+ const idx = this._phones.firstIndexWhere(p => p.phone === phone);
89
+ if (idx >= 0 && this._phones.get(idx).verified_at !== time) {
90
+ const batch = new BatchOperation(this);
91
+ batch.setPathValue([ 'payload', 'phones', idx, 'verified_at' ], time);
92
+ batch.commit();
93
+ } else if (idx < 0) {
94
+ this._phones.push({ phone, verified_at: time });
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,129 @@
1
+ import { Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { CachingHasher, CompactDate, CompactDateTime, MultiClock, MultiTime, Time } from "@shaxpir/shaxpir-common";
3
+ import { ShareSync, ShareSyncFactory } from '../repo';
4
+ import { ArrayView } from './ArrayView';
5
+ import { Content, ContentBody, ContentId, ContentMeta, ContentRef } from "./Content";
6
+ import { ContentKind } from './ContentKind';
7
+ import { TagFilterConfig } from './Device';
8
+ import { BatchOperation } from './Operation';
9
+ import { Session } from './Session';
10
+
11
+ export interface JourneyEntry {
12
+ key:string;
13
+ at_utc_time:CompactDateTime;
14
+ }
15
+
16
+ export interface WorkspacePayload {
17
+ devices:ContentId[];
18
+ sessions:MultiTime[];
19
+ journey?:{[key:string]:CompactDateTime};
20
+ global_tags?:TagFilterConfig;
21
+ }
22
+
23
+ export interface WorkspaceBody extends ContentBody {
24
+ meta:ContentMeta;
25
+ payload:WorkspacePayload;
26
+ }
27
+
28
+ export class Workspace extends Content {
29
+
30
+ private _devicesView:ArrayView<ContentId>;
31
+ private _sessionsView:ArrayView<MultiTime>;
32
+
33
+ constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
34
+ super(doc, shouldAcquire, shareSync);
35
+ this._devicesView = new ArrayView<ContentId>(this, [ 'payload', 'devices' ]);
36
+ this._sessionsView = new ArrayView<MultiTime>(this, [ 'payload', 'sessions' ]);
37
+ }
38
+
39
+ public get payload():WorkspacePayload {
40
+ return this.doc.data.payload;
41
+ }
42
+
43
+ public static makeWorkspaceId(userId:ContentId):ContentId {
44
+ return CachingHasher.makeMd5Base62Hash(userId + "-" + ContentKind.WORKSPACE) as ContentId;
45
+ }
46
+
47
+ public static create(
48
+ userId:ContentId,
49
+ deviceId:ContentId
50
+ ):Workspace {
51
+ const now = MultiClock.now();
52
+ const workspaceId = Workspace.makeWorkspaceId(userId);
53
+ return ShareSyncFactory.get().createContent(
54
+ {
55
+ meta : {
56
+ ref : workspaceId,
57
+ kind : ContentKind.WORKSPACE,
58
+ id : workspaceId,
59
+ owner : userId,
60
+ created_at : now,
61
+ updated_at : now
62
+ },
63
+ payload : {
64
+ devices: [ deviceId ],
65
+ journey: {},
66
+ sessions: [] as MultiTime[]
67
+ }
68
+ }
69
+ ) as Workspace;
70
+ }
71
+
72
+ public get sessions():ArrayView<MultiTime> {
73
+ this.checkDisposed("Workspace.sessions");
74
+ return this._sessionsView;
75
+ }
76
+
77
+ // NOTE: As the user adds devices, we will always add new devices to the end of the array.
78
+ // So the most-recently-added device will always be at the end.
79
+ public get devices():ArrayView<ContentId> {
80
+ this.checkDisposed("Workspace.devices");
81
+ return this._devicesView;
82
+ }
83
+
84
+ public async acquireSessionsFromDate(
85
+ sessionId:ContentId,
86
+ localDate:CompactDate
87
+ ):Promise<Session[]> {
88
+ this.checkDisposed("Workspace.loadSessionsFromDate");
89
+ const shareSync = ShareSyncFactory.get();
90
+ const sessions:Session[] = [];
91
+ for (let i = 0, len = this.sessions.length; i < len; i++) {
92
+ const sessionCreatedAt:MultiTime = this.sessions.get(i);
93
+ const sessionCreatedLocalDate = Time.dateFrom(sessionCreatedAt.local_time);
94
+ if (localDate == sessionCreatedLocalDate) {
95
+ const sessionRef:ContentRef = Session.makeSessionRef(sessionId, sessionCreatedAt);
96
+ const session:Session = await shareSync.acquire(ContentKind.SESSION, sessionRef) as Session;
97
+ sessions.push(session);
98
+ }
99
+ }
100
+ return sessions;
101
+ }
102
+
103
+ public hasJourneyDate(journeyKey:string):boolean {
104
+ this.checkDisposed("Workspace.hasJourneyDate");
105
+ return this.payload.hasOwnProperty('journey') && this.payload.journey.hasOwnProperty(journeyKey);
106
+ }
107
+ public getJourneyDate(journeyKey:string):CompactDateTime {
108
+ this.checkDisposed("Workspace.getJourneyDate");
109
+ if (this.payload.hasOwnProperty('journey')) {
110
+ if (this.payload.journey.hasOwnProperty(journeyKey)) {
111
+ return this.payload.journey[journeyKey];
112
+ }
113
+ }
114
+ return null as CompactDateTime;
115
+ }
116
+ public setJourneyDate(journeyKey:string, journeyDate:CompactDateTime):void {
117
+ this.checkDisposed("Workspace.setJourneyDate");
118
+ const batch:BatchOperation = new BatchOperation(this);
119
+ if (this.payload.hasOwnProperty('journey')) {
120
+ batch.setPathValue([ 'payload', 'journey', journeyKey ], journeyDate);
121
+ } else {
122
+ const journey:any = {};
123
+ journey[journeyKey] = journeyDate;
124
+ batch.setPathValue([ 'payload', 'journey' ], journey);
125
+ }
126
+ batch.commit();
127
+ }
128
+
129
+ }
@@ -1,4 +1,24 @@
1
1
  // created from 'create-ts-index'
2
2
 
3
- export * from './content';
4
- export * from './markup';
3
+ export * from './ArrayView';
4
+ export * from './BayesianScore';
5
+ export * from './ChangeModel';
6
+ export * from './Content';
7
+ export * from './ContentKind';
8
+ export * from './Device';
9
+ export * from './GeoLocation';
10
+ export * from './Hanzi';
11
+ export * from './Manifest';
12
+ export * from './Media';
13
+ export * from './Metric';
14
+ export * from './Model';
15
+ export * from './Operation';
16
+ export * from './Permissions';
17
+ export * from './Phrase';
18
+ export * from './Profile';
19
+ export * from './Progress';
20
+ export * from './Review';
21
+ export * from './Session';
22
+ export * from './Term';
23
+ export * from './User';
24
+ export * from './Workspace';
@@ -1,18 +1,16 @@
1
1
  import ShareDB, { Connection, Doc } from '@shaxpir/sharedb/lib/client';
2
+ import { CompactDateTime } from '@shaxpir/shaxpir-common';
2
3
  import ReconnectingWebSocket from 'reconnecting-websocket';
3
- import { Billing } from "../models/content/Billing";
4
- import { Content, ContentBody, ContentId, ContentRef } from '../models/content/Content';
5
- import { ContentKind } from "../models/content/ContentKind";
6
- import { Device } from '../models/content/Device';
7
- import { Manifest, ManifestBody } from '../models/content/Manifest';
8
- import { Model } from '../models/content/Model';
9
- import { Profile } from '../models/content/Profile';
10
- import { CompactDateTime } from '../models/content/Time';
11
- import { ModernUser } from '../models/content/User';
4
+ import { Model, Profile } from '../models';
5
+ import { Content, ContentBody, ContentId, ContentRef } from '../models/Content';
6
+ import { ContentKind } from '../models/ContentKind';
7
+ import { Device } from '../models/Device';
8
+ import { Manifest, ManifestBody } from '../models/Manifest';
9
+ import { User } from '../models/User';
10
+ import { Encryption } from '../util/Encryption';
12
11
  import { ConnectionListener } from './ConnectionListener';
13
12
 
14
13
  // Register the ShareDB types
15
- import { Encryption } from '../util/Encryption';
16
14
  import * as Json1 from './PermissiveJson1';
17
15
 
18
16
  const RichText = require('@shaxpir/rich-text');
@@ -376,11 +374,9 @@ export class ShareSync {
376
374
  if (kind === ContentKind.MANIFEST) {
377
375
  return new Manifest(doc, shouldAcquire, shareSync);
378
376
  } else if (kind === ContentKind.USER) {
379
- return new ModernUser(doc, shouldAcquire, shareSync);
377
+ return new User(doc, shouldAcquire, shareSync);
380
378
  } else if (kind === ContentKind.DEVICE) {
381
379
  return new Device(doc, shouldAcquire, shareSync);
382
- } else if (kind === ContentKind.BILLING) {
383
- return new Billing(doc, shouldAcquire, shareSync);
384
380
  } else if (kind === ContentKind.PROFILE) {
385
381
  return new Profile(doc, shouldAcquire, shareSync);
386
382
  }
package/lib/repo/index.ts CHANGED
@@ -3,3 +3,4 @@
3
3
  export * from './ConnectionListener';
4
4
  export * from './PermissiveJson1';
5
5
  export * from './ShareSync';
6
+ export * from './TextEditOps';
@@ -1,5 +1,5 @@
1
1
  export interface Encryption {
2
- getRandomString(length:number):string;
2
+ getRandomString(chars:string, length:number):string;
3
3
  encrypt(cleartext:string, key:string):string;
4
4
  decrypt(cyphertext:string, key:string):string;
5
5
  }
package/lib/util/index.ts CHANGED
@@ -1,10 +1,4 @@
1
1
  // created from 'create-ts-index'
2
2
 
3
- export * from './Base62';
4
- export * from './CachingHasher';
5
- export * from './Dispatch';
6
3
  export * from './Encryption';
7
4
  export * from './Logging';
8
- export * from './NumberFormat';
9
- export * from './Struct';
10
- export * from './Tuples';