@shaxpir/duiduidui-models 1.0.1 → 1.1.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 (224) 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/index.d.ts +0 -6
  47. package/dist/util/index.js +0 -6
  48. package/lib/models/{content/ArrayView.ts → ArrayView.ts} +2 -2
  49. package/lib/models/BayesianScore.ts +32 -0
  50. package/lib/models/{markup/ChangeModel.ts → ChangeModel.ts} +3 -4
  51. package/lib/models/{content/Content.ts → Content.ts} +15 -18
  52. package/lib/models/{content/ContentKind.ts → ContentKind.ts} +6 -1
  53. package/lib/models/{content/Device.ts → Device.ts} +26 -22
  54. package/lib/models/Hanzi.ts +16 -0
  55. package/lib/models/{content/Manifest.ts → Manifest.ts} +15 -6
  56. package/lib/models/{content/Media.ts → Media.ts} +3 -4
  57. package/lib/models/Metric.ts +233 -0
  58. package/lib/models/{content/Model.ts → Model.ts} +4 -6
  59. package/lib/models/{markup/Operation.ts → Operation.ts} +6 -16
  60. package/lib/models/{content/Permissions.ts → Permissions.ts} +0 -2
  61. package/lib/models/Phrase.ts +53 -0
  62. package/lib/models/{content/Profile.ts → Profile.ts} +4 -6
  63. package/lib/models/Progress.ts +66 -0
  64. package/lib/models/Review.ts +18 -0
  65. package/lib/models/Session.ts +140 -0
  66. package/lib/models/Term.ts +139 -0
  67. package/lib/models/User.ts +97 -0
  68. package/lib/models/Workspace.ts +129 -0
  69. package/lib/models/index.ts +22 -2
  70. package/lib/repo/ShareSync.ts +9 -13
  71. package/lib/repo/index.ts +1 -0
  72. package/lib/util/index.ts +0 -6
  73. package/package.json +4 -15
  74. package/dist/models/OutboundMessage.d.ts +0 -18
  75. package/dist/models/OutboundMessage.js +0 -25
  76. package/dist/models/content/Activity.d.ts +0 -10
  77. package/dist/models/content/Billing.d.ts +0 -144
  78. package/dist/models/content/Billing.js +0 -418
  79. package/dist/models/content/Book.d.ts +0 -77
  80. package/dist/models/content/Book.js +0 -407
  81. package/dist/models/content/Category.d.ts +0 -16
  82. package/dist/models/content/Category.js +0 -20
  83. package/dist/models/content/Checkpointable.d.ts +0 -21
  84. package/dist/models/content/Checkpointable.js +0 -156
  85. package/dist/models/content/Comment.d.ts +0 -19
  86. package/dist/models/content/Comment.js +0 -53
  87. package/dist/models/content/ConceptArt.d.ts +0 -31
  88. package/dist/models/content/ConceptArt.js +0 -84
  89. package/dist/models/content/Context.d.ts +0 -28
  90. package/dist/models/content/Context.js +0 -162
  91. package/dist/models/content/DevEnv.d.ts +0 -5
  92. package/dist/models/content/DevEnv.js +0 -9
  93. package/dist/models/content/Dictionary.d.ts +0 -31
  94. package/dist/models/content/Dictionary.js +0 -5
  95. package/dist/models/content/DictionaryEntry.d.ts +0 -20
  96. package/dist/models/content/DictionaryEntry.js +0 -2
  97. package/dist/models/content/ElasticModel.d.ts +0 -149
  98. package/dist/models/content/ElasticModel.js +0 -179
  99. package/dist/models/content/Environment.d.ts +0 -61
  100. package/dist/models/content/Environment.js +0 -124
  101. package/dist/models/content/ExportOptions.d.ts +0 -64
  102. package/dist/models/content/ExportOptions.js +0 -213
  103. package/dist/models/content/Folder.d.ts +0 -16
  104. package/dist/models/content/Folder.js +0 -33
  105. package/dist/models/content/Fragment.d.ts +0 -54
  106. package/dist/models/content/Fragment.js +0 -181
  107. package/dist/models/content/Hanzi.d.ts +0 -21
  108. package/dist/models/content/HighlightRule.d.ts +0 -9
  109. package/dist/models/content/HighlightRule.js +0 -2
  110. package/dist/models/content/Migration.d.ts +0 -68
  111. package/dist/models/content/Migration.js +0 -155
  112. package/dist/models/content/Phrase.d.ts +0 -8
  113. package/dist/models/content/Placeholder.d.ts +0 -8
  114. package/dist/models/content/Placeholder.js +0 -36
  115. package/dist/models/content/RichText.d.ts +0 -58
  116. package/dist/models/content/RichText.js +0 -79
  117. package/dist/models/content/Session.js +0 -173
  118. package/dist/models/content/Speech.d.ts +0 -67
  119. package/dist/models/content/Speech.js +0 -97
  120. package/dist/models/content/Stub.d.ts +0 -24
  121. package/dist/models/content/Stub.js +0 -179
  122. package/dist/models/content/Time.d.ts +0 -56
  123. package/dist/models/content/Time.js +0 -295
  124. package/dist/models/content/User.d.ts +0 -36
  125. package/dist/models/content/User.js +0 -95
  126. package/dist/models/content/Workspace.d.ts +0 -71
  127. package/dist/models/content/Workspace.js +0 -237
  128. package/dist/models/content/index.d.ts +0 -17
  129. package/dist/models/content/index.js +0 -34
  130. package/dist/models/legacy/LegacyBodyFormat.d.ts +0 -9
  131. package/dist/models/legacy/LegacyBodyFormat.js +0 -2
  132. package/dist/models/legacy/LegacyComment.d.ts +0 -12
  133. package/dist/models/legacy/LegacyComment.js +0 -2
  134. package/dist/models/legacy/LegacyContent.d.ts +0 -53
  135. package/dist/models/legacy/LegacyContent.js +0 -55
  136. package/dist/models/legacy/LegacyConversion.d.ts +0 -55
  137. package/dist/models/legacy/LegacyConversion.js +0 -401
  138. package/dist/models/legacy/LegacyFragment.d.ts +0 -21
  139. package/dist/models/legacy/LegacyFragment.js +0 -2
  140. package/dist/models/legacy/LegacyLocator.d.ts +0 -8
  141. package/dist/models/legacy/LegacyLocator.js +0 -31
  142. package/dist/models/legacy/LegacyOutboundMessage.d.ts +0 -16
  143. package/dist/models/legacy/LegacyOutboundMessage.js +0 -13
  144. package/dist/models/legacy/LegacyPicture.d.ts +0 -14
  145. package/dist/models/legacy/LegacyPicture.js +0 -2
  146. package/dist/models/legacy/LegacyProfile.d.ts +0 -9
  147. package/dist/models/legacy/LegacyProfile.js +0 -2
  148. package/dist/models/legacy/LegacySession.d.ts +0 -41
  149. package/dist/models/legacy/LegacySession.js +0 -35
  150. package/dist/models/legacy/LegacyStory.d.ts +0 -23
  151. package/dist/models/legacy/LegacyStory.js +0 -2
  152. package/dist/models/legacy/LegacyStub.d.ts +0 -15
  153. package/dist/models/legacy/LegacyStub.js +0 -2
  154. package/dist/models/legacy/LegacyTransaction.d.ts +0 -14
  155. package/dist/models/legacy/LegacyTransaction.js +0 -49
  156. package/dist/models/legacy/LegacyUser.d.ts +0 -28
  157. package/dist/models/legacy/LegacyUser.js +0 -32
  158. package/dist/models/legacy/LegacyWorkspace.d.ts +0 -23
  159. package/dist/models/legacy/LegacyWorkspace.js +0 -6
  160. package/dist/models/legacy/index.d.ts +0 -15
  161. package/dist/models/legacy/index.js +0 -32
  162. package/dist/models/markup/BodyFormat.d.ts +0 -14
  163. package/dist/models/markup/BodyFormat.js +0 -190
  164. package/dist/models/markup/DeltaOps.d.ts +0 -5
  165. package/dist/models/markup/DeltaOps.js +0 -74
  166. package/dist/models/markup/HtmlMarkup.d.ts +0 -4
  167. package/dist/models/markup/HtmlMarkup.js +0 -21
  168. package/dist/models/markup/index.d.ts +0 -4
  169. package/dist/models/markup/index.js +0 -21
  170. package/dist/util/Async.d.ts +0 -8
  171. package/dist/util/Async.js +0 -18
  172. package/dist/util/Base62.d.ts +0 -6
  173. package/dist/util/Base62.js +0 -47
  174. package/dist/util/BinarySearch.d.ts +0 -7
  175. package/dist/util/BinarySearch.js +0 -46
  176. package/dist/util/CachingHasher.d.ts +0 -8
  177. package/dist/util/CachingHasher.js +0 -41
  178. package/dist/util/Color.d.ts +0 -32
  179. package/dist/util/Color.js +0 -204
  180. package/dist/util/Dispatch.d.ts +0 -15
  181. package/dist/util/Dispatch.js +0 -79
  182. package/dist/util/EditDistance.d.ts +0 -13
  183. package/dist/util/EditDistance.js +0 -184
  184. package/dist/util/NumberFormat.d.ts +0 -14
  185. package/dist/util/NumberFormat.js +0 -224
  186. package/dist/util/Struct.d.ts +0 -4
  187. package/dist/util/Struct.js +0 -15
  188. package/dist/util/Template.d.ts +0 -16
  189. package/dist/util/Template.js +0 -128
  190. package/dist/util/Text.d.ts +0 -45
  191. package/dist/util/Text.js +0 -243
  192. package/dist/util/Tuples.d.ts +0 -9
  193. package/dist/util/Tuples.js +0 -135
  194. package/dist/util/Validate.d.ts +0 -4
  195. package/dist/util/Validate.js +0 -11
  196. package/dist/util/Vocabulary.d.ts +0 -3
  197. package/dist/util/Vocabulary.js +0 -35
  198. package/lib/models/content/Billing.ts +0 -558
  199. package/lib/models/content/DevEnv.ts +0 -5
  200. package/lib/models/content/DictionaryEntry.ts +0 -22
  201. package/lib/models/content/Hanzi.ts +0 -25
  202. package/lib/models/content/Phrase.ts +0 -10
  203. package/lib/models/content/Time.ts +0 -328
  204. package/lib/models/content/User.ts +0 -130
  205. package/lib/models/content/index.ts +0 -19
  206. package/lib/models/markup/DeltaOps.ts +0 -71
  207. package/lib/models/markup/index.ts +0 -6
  208. package/lib/util/Base62.ts +0 -47
  209. package/lib/util/CachingHasher.ts +0 -38
  210. package/lib/util/Dispatch.ts +0 -92
  211. package/lib/util/NumberFormat.ts +0 -194
  212. package/lib/util/Struct.ts +0 -14
  213. package/lib/util/Tuples.ts +0 -131
  214. /package/dist/models/{content/GeoLocation.d.ts → GeoLocation.d.ts} +0 -0
  215. /package/dist/models/{content/GeoLocation.js → GeoLocation.js} +0 -0
  216. /package/dist/models/{content/Hanzi.js → Hanzi.js} +0 -0
  217. /package/dist/models/{content/Permissions.d.ts → Permissions.d.ts} +0 -0
  218. /package/dist/models/{content/Permissions.js → Permissions.js} +0 -0
  219. /package/dist/models/{content/Phrase.js → Phrase.js} +0 -0
  220. /package/dist/models/{content/Activity.js → Review.js} +0 -0
  221. /package/dist/{models/markup → repo}/TextEditOps.d.ts +0 -0
  222. /package/dist/{models/markup → repo}/TextEditOps.js +0 -0
  223. /package/lib/models/{content/GeoLocation.ts → GeoLocation.ts} +0 -0
  224. /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';
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';