@media-quest/engine 0.0.14 → 0.0.15

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 (60) hide show
  1. package/package.json +1 -2
  2. package/src/Delement/DElement.dto.ts +5 -0
  3. package/src/Delement/DElement.ts +78 -253
  4. package/src/Delement/DImg.ts +31 -70
  5. package/src/Delement/DStyle.ts +156 -156
  6. package/src/Delement/DText.ts +9 -25
  7. package/src/Delement/Ddiv.ts +10 -23
  8. package/src/Delement/button-click-action.ts +11 -0
  9. package/src/Delement/element-factory.ts +40 -0
  10. package/src/engine/DCommand.ts +21 -0
  11. package/src/engine/SchemaDto.ts +24 -0
  12. package/src/engine/SchemaEngine.ts +48 -120
  13. package/src/{player → engine}/dplayer.spec.ts +20 -17
  14. package/src/{player → engine}/dplayer.ts +29 -16
  15. package/src/engine/history-que.spec.ts +59 -0
  16. package/src/engine/history-que.ts +39 -0
  17. package/src/{player → engine}/next-que.spec.ts +19 -8
  18. package/src/engine/next-que.ts +93 -0
  19. package/src/page/Page.ts +160 -0
  20. package/src/page/media-player.ts +117 -0
  21. package/src/page/page-component.ts +113 -0
  22. package/src/page/page-result.ts +11 -0
  23. package/src/page/task-manager.ts +203 -0
  24. package/src/page/task-state.ts +55 -0
  25. package/src/page/task.ts +87 -0
  26. package/src/public-api.ts +17 -13
  27. package/src/rules/__test__/rule-engine.spec.ts +1 -1
  28. package/src/utils/DUtil.ts +110 -103
  29. package/tsconfig.tsbuildinfo +1 -0
  30. package/dist/public-api.js +0 -2245
  31. package/dist/public-api.mjs +0 -2205
  32. package/src/Delement/AudioContainer.ts +0 -169
  33. package/src/Delement/DAuto-play.ts +0 -36
  34. package/src/Delement/VideoContainer.ts +0 -199
  35. package/src/commands/DCommand.ts +0 -63
  36. package/src/commands/DCommandBus.ts +0 -60
  37. package/src/dto/AnimationDto.ts +0 -4
  38. package/src/dto/DElement.dto.ts +0 -46
  39. package/src/dto/SchemaDto.ts +0 -65
  40. package/src/engine/DPage.ts +0 -60
  41. package/src/engine/element-factory.ts +0 -52
  42. package/src/event-handlers/DEventHandler.ts +0 -29
  43. package/src/events/DEvents.ts +0 -94
  44. package/src/events/event-bus.spec.ts +0 -21
  45. package/src/events/event-bus.ts +0 -81
  46. package/src/kladd/context-menu-manager.ts +0 -56
  47. package/src/player/history-que.spec.ts +0 -46
  48. package/src/player/history-que.ts +0 -38
  49. package/src/player/next-que.ts +0 -93
  50. package/src/services/DMedia-manager.spec.ts +0 -27
  51. package/src/services/DMedia-manager.ts +0 -179
  52. package/src/services/resource-provider.ts +0 -33
  53. package/src/services/sequence-manager.spec.ts +0 -168
  54. package/src/services/sequence-manager.ts +0 -133
  55. package/src/state/Dstate.spec.ts +0 -7
  56. package/src/state/Dstate.ts +0 -105
  57. package/src/state/boolean-property.ts +0 -69
  58. package/src/state/state-service.spec.ts +0 -307
  59. package/src/state/state-service.ts +0 -251
  60. package/src/state/state-testing-helpers.ts +0 -59
@@ -1,51 +1,39 @@
1
- import { SchemaDto } from "../dto/SchemaDto";
2
- import { DMediaManager } from "../services/DMedia-manager";
3
- import { DCommandBus } from "../commands/DCommandBus";
4
- import { EventBus } from "../events/event-bus";
5
- import { DPlayer } from "../player/dplayer";
6
- import { AnsweredQuestion } from "../player/history-que";
7
- import { DTimestamp } from "../common/DTimestamp";
8
- import { DPage } from "./DPage";
1
+ import { SchemaDto } from "./SchemaDto";
2
+ import { DPlayer } from "./dplayer";
9
3
  import { ScaleService } from "./scale";
10
- import { ResourceProvider } from "../services/resource-provider";
11
- import { StateService } from "../state/state-service";
12
- import { DCommand, StateCommand } from "../commands/DCommand";
13
- import { DEvent } from "../events/DEvents";
4
+ // import { DEvent } from "../events/DEvents";
5
+ import { Page } from "../page/Page";
6
+ import { TaskManager } from "../page/task-manager";
7
+ // import { AnsweredQuestion, PageHistory } from "./history-que";
8
+ import { PageResult } from "../page/page-result";
14
9
 
15
10
  export interface EngineLogger {
16
11
  error(message: string): void;
17
12
  warn(message: string): void;
18
- appEvent(event: DEvent): void;
19
- appCommand(command: DCommand): void;
13
+ // appEvent(event: DEvent): void;
20
14
  }
21
15
 
22
16
  export interface SchemaResult {
23
- readonly eventLog: ReadonlyArray<DEvent>;
24
- readonly commandLog: ReadonlyArray<DCommand>;
17
+ readonly eventLog: ReadonlyArray<any>;
25
18
  readonly answers: ReadonlyArray<any>;
26
19
  }
27
20
  export interface ISchemaEngine {
28
21
  onComplete(handler: (result: SchemaResult) => void): void;
29
- onCommandOrEvent(item: DEvent | DCommand): void;
22
+ // onCommandOrEvent(item: DEvent | DCommand): void;
30
23
  setSchema(schema: SchemaDto): void;
31
24
  onFatalError(handler: (error: { message: string }) => void): void;
32
25
  }
33
26
 
34
27
  export class SchemaEngine implements ISchemaEngine {
35
28
  private readonly TAG = "[ SCHEMA_ENGINE ] :";
36
- private readonly commandBus = new DCommandBus();
37
- private readonly eventBus = new EventBus();
38
- private readonly mediaManager: DMediaManager;
39
29
  private readonly scale: ScaleService;
40
30
  private readonly hostElement: HTMLDivElement;
41
- private readonly uiContainer: HTMLDivElement = document.createElement("div");
42
- private readonly mediaContainer: HTMLDivElement = document.createElement("div");
43
- private readonly resourceProvider: ResourceProvider;
44
- private readonly stateService: StateService;
45
- private readonly globalEventToStateHandlers = new Map<string, ReadonlyArray<StateCommand>>();
31
+ private readonly taskManager: TaskManager;
32
+ private readonly uiLayer: HTMLDivElement = document.createElement("div");
33
+ private readonly mediaLayer: HTMLDivElement = document.createElement("div");
46
34
  private player: DPlayer;
47
- private currentPage: DPage | false = false;
48
- private readonly subs: Array<() => void> = [];
35
+ private currentPage: Page | false = false;
36
+ private readonly tickerRef: number | false = false;
49
37
 
50
38
  constructor(
51
39
  hostEl: HTMLDivElement,
@@ -53,79 +41,32 @@ export class SchemaEngine implements ISchemaEngine {
53
41
  private readonly width: number,
54
42
  private readonly schema: SchemaDto,
55
43
  ) {
44
+ this.tickerRef = window.setInterval(() => {
45
+ if (this.currentPage) {
46
+ this.currentPage.tick();
47
+ }
48
+ }, 100);
56
49
  this.hostElement = hostEl;
57
- this.hostElement.appendChild(this.uiContainer);
58
- this.hostElement.appendChild(this.mediaContainer);
59
- const stateProps = this.schema.stateProps ?? [];
60
- const stateQueries = this.schema.stateQueries ?? [];
61
- this.stateService = new StateService(this.eventBus, this.commandBus, stateProps, stateQueries);
50
+ this.hostElement.appendChild(this.mediaLayer);
51
+ this.hostElement.appendChild(this.uiLayer);
62
52
  this.scale = new ScaleService({
63
53
  baseHeight: schema.baseHeight,
64
54
  baseWidth: schema.baseWidth,
65
55
  containerWidth: width,
66
56
  containerHeight: height,
67
57
  });
68
- // this.commandBus.logCommands = true;
69
- const globalEventHandlers = schema.stateFromEvent ?? [];
70
-
71
- globalEventHandlers.forEach((h) => {
72
- this.globalEventToStateHandlers.set(h.onEvent, h.thenExecute);
73
- });
74
-
75
- const resources = SchemaDto.getResources(this.schema);
76
- this.resourceProvider = new ResourceProvider({ videos: resources.videoList, audio: resources.audioList });
77
- this.mediaManager = new DMediaManager(
78
- this.mediaContainer,
79
- this.commandBus,
80
- this.eventBus,
81
- this.resourceProvider,
82
- this.scale,
83
- );
84
58
  this.player = new DPlayer(this.schema);
59
+ this.taskManager = new TaskManager(this.mediaLayer, this.scale, (error) => {
60
+ console.log(error);
61
+ });
85
62
  this.styleSelf();
63
+ this.handlePageCompleted = this.handlePageCompleted.bind(this);
86
64
  this.nextPage();
87
- this.hookUpListeners();
88
65
  }
89
66
 
90
- private hookUpListeners() {
91
- const eventSubscription = this.eventBus.subscribe((ev) => {
92
- this.onCommandOrEvent(ev);
93
- const globalHandlers = this.globalEventToStateHandlers.get(ev.kind) ?? [];
94
- globalHandlers.forEach((stateCommand) => {
95
- this.commandBus.emit(stateCommand);
96
- });
97
- }, this.TAG + "HOOK_UP_LISTENERS");
98
- const commandSubscription = this.commandBus.subscribe((command) => {
99
- // switch (command.kind) {
100
- //
101
- // }
102
- this.onCommandOrEvent(command);
103
- if (command.kind === "PAGE_QUE_NEXT_PAGE_COMMAND") {
104
- this.nextPage();
105
- }
106
-
107
- if (command.kind === "ENGINE_LEAVE_PAGE_COMMAND") {
108
- console.log(this.TAG + "SEQUENCE STARTED -- TODO EVENT FOR THIS??");
109
- console.log(command);
110
- const pageId = command.payload.pageId;
111
- const facts = command.payload.factsCollected;
112
- const timestamp = DTimestamp.now();
113
- const ans: AnsweredQuestion[] = facts.map((f) => ({
114
- timestamp,
115
- fact: f,
116
- }));
117
- this.player.saveHistory({
118
- answeredQuestions: ans,
119
- pageId,
120
- });
121
-
122
- this.nextPage();
123
- // const history: PageHistory = { page: {}, answeredQuestions: [] };
124
- }
125
- }, this.TAG);
126
-
127
- this.subs.push(commandSubscription);
128
- this.subs.push(eventSubscription);
67
+ private handlePageCompleted(result: PageResult) {
68
+ this.player.saveHistory(result);
69
+ this.nextPage();
129
70
  }
130
71
 
131
72
  private styleSelf() {
@@ -140,61 +81,48 @@ export class SchemaEngine implements ISchemaEngine {
140
81
  div.style.position = "static";
141
82
  };
142
83
 
143
- makeStatic(this.uiContainer);
144
- makeStatic(this.mediaContainer);
84
+ makeStatic(this.uiLayer);
85
+ this.uiLayer.style.zIndex = "10";
86
+ this.mediaLayer.style.zIndex = "8";
87
+ makeStatic(this.mediaLayer);
145
88
  }
146
89
 
147
90
  private nextPage() {
148
- // TODO SHOULD THIS BE PART OF THE SCHEMA?? THIS IS CLEANUP LOGIC
149
- // this.commandBus.emit({ kind: "VIDEO_PAUSE_COMMAND", target: "VIDEO", targetId: "VIDEO", payload: {} });
150
- // this.commandBus.emit({ kind: "AUDIO_PAUSE_COMMAND", target: "AUDIO", targetId: "AUDIO", payload: {} });
151
- // const currPageId = this.currentPage ? this.currentPage.id : "NO_PAGE";
152
- // console.groupCollapsed("NEXT_PAGE FROM: " + currPageId);
153
- // console.log(this.TAG + " NEXT_PAGE STARTED AT: " + currPageId);
154
- this.mediaManager.clearAllMedia();
155
91
  const nextPage = this.player.getNextPage();
156
- // const state = this.stateService.getState();
157
- // console.log(state);
158
- // TODO CLEAN UP PAGE COMPONENTS this.page.CleanUp()
159
92
  if (this.currentPage) {
160
93
  this.currentPage.destroy();
161
- this.uiContainer.innerHTML = "";
94
+
95
+ this.uiLayer.innerHTML = "";
162
96
  }
97
+
163
98
  if (!nextPage) {
99
+ // console.log("NO MORE PAGES");
164
100
  // TODO FIGURE OUT WHAQT TO DO AT END OF TEST!! Start over??
165
101
  this.player = new DPlayer(this.schema);
166
- if (this.schema.pages.length > 0) {
102
+ if (this.player.pageCount > 0) {
167
103
  this.nextPage();
168
104
  }
169
105
  return false;
170
106
  }
171
- const newPage = new DPage(nextPage, this.eventBus, this.commandBus, this.scale);
107
+
108
+ const newPage = new Page(nextPage, this.taskManager, this.scale, (result) => {
109
+ this.handlePageCompleted(result);
110
+ });
111
+
112
+ // console.log("APPENDING PAGE");
172
113
 
173
114
  this.currentPage = newPage;
174
- newPage.appendYourself(this.uiContainer);
115
+ // this.uiContainer.innerHTML = "";
175
116
 
176
- this.mediaManager.setPage(nextPage);
177
- const s1 = this.stateService.getState();
178
- // console.log(s1);
179
- // console.log("Next-page: " + newPage.id);
180
- // console.groupEnd();
117
+ newPage.appendYourself(this.uiLayer);
181
118
  return true;
182
119
  }
183
120
 
184
121
  destroy() {
185
122
  if (this.currentPage) {
186
123
  this.currentPage.destroy();
187
- this.uiContainer.innerHTML = "";
124
+ this.uiLayer.innerHTML = "";
188
125
  }
189
- this.mediaManager.destroy();
190
- this.stateService.destroy();
191
- this.subs.forEach((sub) => {
192
- sub();
193
- });
194
- const evStats = this.eventBus.getStats();
195
- const cmdStats = this.commandBus.getStats();
196
- console.assert(evStats.subscribersCount === 0, this.TAG + " Eventbus should have no subscribers ", evStats);
197
- console.assert(cmdStats.subscribersCount === 0, this.TAG + "Commandbus should have no subscribers", cmdStats);
198
126
  }
199
127
  onComplete(handler: (result: SchemaResult) => void) {
200
128
  console.log(handler);
@@ -204,7 +132,7 @@ export class SchemaEngine implements ISchemaEngine {
204
132
  console.log(handler);
205
133
  }
206
134
 
207
- onCommandOrEvent(_event_or_command: DEvent | DCommand) {}
135
+ onCommandOrEvent(_event_or_command: any) {}
208
136
 
209
137
  setSchema(schema: SchemaDto): void {
210
138
  console.log(schema);
@@ -1,13 +1,15 @@
1
1
  import { DPlayer, DPlayerData } from "./dplayer";
2
- import { PageDto, PageSequenceDto } from "../dto/SchemaDto";
2
+ import { PageSequenceDto } from "./SchemaDto";
3
3
  import { Rule } from "../rules/rule";
4
- import { PageHistory } from "./history-que";
4
+ // import { PageHistory } from "./history-que";
5
5
  import { DTimestamp } from "../common/DTimestamp";
6
- import { PageQueCommand } from "../commands/DCommand";
7
- import { PageID } from "../utils/ID";
6
+ import { PageQueCommand } from "./DCommand";
7
+ import { PageDto } from "../page/Page";
8
+ import { PageResult } from "../page/page-result";
8
9
 
9
10
  const page = (id: number): PageDto => {
10
- return { id: PageID.create(), elements: [] };
11
+ return PageDto.createDummy(id);
12
+ // return { id: PageID.create(), elements: [] };
11
13
  };
12
14
  const p1 = page(1);
13
15
  const p2 = page(2);
@@ -31,7 +33,7 @@ const data = (
31
33
  pages: PageDto[],
32
34
  pageSequences: PageSequenceDto[] = [],
33
35
  rules: Rule<PageQueCommand, never>[] = [],
34
- ): DPlayerData => ({ pages, pageSequences, rules });
36
+ ): DPlayerData => ({ pages: pages, pageSequences, rules });
35
37
  // const seq = (pages: PageDto[], rules: Rule[]) => {};
36
38
  beforeEach(() => {
37
39
  // data = { pages: [], rules: [], pageSequences: [] };
@@ -85,24 +87,25 @@ describe("DPlayer", () => {
85
87
 
86
88
  it("Save history", () => {
87
89
  const player = new DPlayer(data(all));
90
+
88
91
  const curr = player.getNextPage() as PageDto;
89
92
 
90
- const history: PageHistory = {
93
+ const result: PageResult = {
91
94
  pageId: curr.id,
92
- answeredQuestions: [
95
+ pageEntered: DTimestamp.now(),
96
+ pageExited: DTimestamp.now(),
97
+ pageTime: DTimestamp.diff(DTimestamp.now(), DTimestamp.now()),
98
+ collectedFacts: [
93
99
  {
94
- timestamp: DTimestamp.now(),
95
- fact: {
96
- referenceId: "as",
97
- referenceLabel: "as-label",
98
- kind: "numeric-fact",
99
- label: "litt",
100
- value: 2,
101
- },
100
+ referenceId: "as",
101
+ referenceLabel: "as-label",
102
+ kind: "numeric-fact",
103
+ label: "litt",
104
+ value: 2,
102
105
  },
103
106
  ],
104
107
  };
105
- player.saveHistory(history);
108
+ player.saveHistory(result);
106
109
  expect(player.getNextPage()).toBe(p2);
107
110
  expect(player.getResults().length).toBe(1);
108
111
  });
@@ -1,31 +1,35 @@
1
- import { AnsweredQuestion, HistoryQue, PageHistory } from "./history-que";
1
+ import { HistoryQue } from "./history-que";
2
2
  import { RuleEngine } from "../rules/rule-engine";
3
3
  import { NextQue } from "./next-que";
4
- import { PageDto, SchemaDto } from "../dto/SchemaDto";
5
- import { NavigationCommand, PageQueCommand } from "../commands/DCommand";
4
+ import { SchemaDto } from "./SchemaDto";
5
+ import { NavigationCommand, PageQueCommand } from "./DCommand";
6
6
  import { DUtil } from "../utils/DUtil";
7
+ import { PageDto } from "../page/Page";
8
+ import { PageResult } from "../page/page-result";
9
+ import { Fact } from "../rules/fact";
7
10
 
8
11
  export type DPlayerData = Pick<SchemaDto, "pages" | "pageSequences" | "rules">;
9
12
  export class DPlayer {
10
13
  private history = new HistoryQue();
11
- private ruleEngine = new RuleEngine<PageQueCommand, PageQueCommand>();
14
+ private ruleEngine = new RuleEngine<PageQueCommand, never>();
12
15
  private nextQue = new NextQue();
13
16
  private data: DPlayerData;
14
17
 
15
18
  constructor(data: DPlayerData) {
16
19
  this.data = data;
17
- this.nextQue.resetQue(data.pages);
20
+ const pages = data.pages ?? [];
21
+ this.nextQue.resetQue(pages);
18
22
  }
19
23
 
20
- saveHistory(pageHistory: PageHistory) {
21
- console.log("SAVE HISTORY", pageHistory);
24
+ saveHistory(pageHistory: PageResult) {
25
+ // console.log("SAVE HISTORY", pageHistory);
22
26
  this.history.addToHistory(pageHistory);
23
27
  const facts = this.history.getFacts();
24
28
  const result = this.ruleEngine.solveAll(this.data.rules, facts);
25
29
  const matchingRules = result.matching;
26
30
  const actions = matchingRules.map((r) => r.actionList).flat(1);
27
31
  actions.forEach((a) => {
28
- console.log(a.payload);
32
+ // console.log(a.payload);
29
33
  switch (a.kind) {
30
34
  case "PAGE_QUE_JUMP_TO_PAGE_COMMAND":
31
35
  this.nextQue.jumpToPageById(a.payload.pageId);
@@ -41,11 +45,11 @@ export class DPlayer {
41
45
  const check: never = a;
42
46
  }
43
47
  });
44
- console.log(actions);
48
+ // console.log(actions);
45
49
  }
46
50
 
47
- getResults(): AnsweredQuestion[] {
48
- return this.history.getAnswers();
51
+ getResults(): Fact[] {
52
+ return this.history.getFacts();
49
53
  }
50
54
 
51
55
  private goToPageById(pageId: string) {
@@ -54,9 +58,6 @@ export class DPlayer {
54
58
 
55
59
  handleNavigationCommand(command: NavigationCommand) {
56
60
  switch (command.kind) {
57
- case "PAGE_QUE_NEXT_PAGE_COMMAND":
58
- // NO NEED TO DO ANYTHING
59
- break;
60
61
  case "PAGE_QUE_GO_TO_PAGE_COMMAND":
61
62
  this.goToPageById(command.payload.pageId);
62
63
  break;
@@ -69,9 +70,21 @@ export class DPlayer {
69
70
  }
70
71
  }
71
72
 
73
+ // getNextPage():
74
+ // | { kind: "first-page"; readonly page: PageDto }
75
+ // | { kind: "next-page"; readonly page: PageDto }
76
+ // | { kind: "no-more-pages" } {
77
+ // const next = this.nextQue.pop();
78
+ // const a = this.nextQue.
79
+ // if (next) {
80
+ // return { kind: "next-page", page: next };
81
+ // }
82
+ // return { kind: "no-more-pages" };
83
+ // }
84
+
72
85
  getNextPage(): PageDto | false {
73
- // TODO HANDLE RULES!!
74
- return this.nextQue.pop();
86
+ const next = this.nextQue.pop();
87
+ return next ?? false;
75
88
  }
76
89
 
77
90
  private insertSequenceById(id: string) {
@@ -0,0 +1,59 @@
1
+ import { HistoryQue } from "./history-que";
2
+ import { DTimestamp } from "../common/DTimestamp";
3
+ import { PageID } from "../utils/ID";
4
+ import { PageDto } from "../page/Page";
5
+ import { PageResult } from "../page/page-result";
6
+
7
+ const p = (id: number): PageDto => {
8
+ return { id: PageID.create(), components: [], initialTasks: [], staticElements: [], background: "", tags: [] };
9
+ };
10
+
11
+ const pageResult = (id: number, value: number): PageResult => {
12
+ const pageEntered = DTimestamp.now();
13
+ const pageExited = DTimestamp.addMills(pageEntered, 1000);
14
+ const pageTime = DTimestamp.diff(pageEntered, pageExited);
15
+
16
+ const result: PageResult = {
17
+ pageId: "_dummyId" + id,
18
+ pageEntered,
19
+ pageExited,
20
+ pageTime,
21
+ collectedFacts: [
22
+ {
23
+ referenceId: "" + id,
24
+ referenceLabel: "label-for-" + id,
25
+ value,
26
+ label: "value-label " + value,
27
+ kind: "numeric-fact",
28
+ },
29
+ ],
30
+ };
31
+ return result;
32
+ };
33
+ // const h = (page: PageDto, answeredQuestions: AnsweredQuestion[]): PageResult => ({
34
+ // pageId: page.id,
35
+ // answeredQuestions,
36
+ // });
37
+ const p1 = p(1);
38
+ const p2 = p(2);
39
+ const p3 = p(3);
40
+ const p4 = p(4);
41
+ const p5 = p(5);
42
+ const p6 = p(6);
43
+ const all = [p1, p2, p3, p4, p5, p6];
44
+
45
+ let history = new HistoryQue();
46
+ beforeEach(() => {
47
+ history = new HistoryQue();
48
+ });
49
+
50
+ describe("HistoryQue", () => {
51
+ it("should create an instance", () => {
52
+ expect(history).toBeTruthy();
53
+ });
54
+
55
+ it("Can add history, and get facts back", () => {
56
+ history.addToHistory(pageResult(1, 2));
57
+ expect(history.getFacts().length).toBe(1);
58
+ });
59
+ });
@@ -0,0 +1,39 @@
1
+ import { Fact } from "../rules/fact";
2
+ import { DTimestamp } from "../common/DTimestamp";
3
+ import { PageResult } from "../page/page-result";
4
+
5
+ // export interface AnsweredQuestion {
6
+ // readonly timestamp: DTimestamp;
7
+ // readonly fact: Fact;
8
+ // }
9
+
10
+ // export namespace AnsweredQuestion {
11
+ // export const eq = (a: AnsweredQuestion, b: AnsweredQuestion): boolean => {
12
+ // return a.fact === b.fact;
13
+ // };
14
+ // }
15
+
16
+ // export interface PageHistory {
17
+ // readonly pageId: string;
18
+ // readonly answeredQuestions: AnsweredQuestion[];
19
+ // }
20
+
21
+ export class HistoryQue {
22
+ private history: PageResult[] = [];
23
+
24
+ getFacts(): Array<Fact> {
25
+ const answers = this.history.map((h) => h.collectedFacts).flat(1);
26
+ // const facts = answers.map((a) => a.fact);
27
+ // TODO FIND LATEST FACT (answer) if have multiple.
28
+ return answers;
29
+ }
30
+
31
+ // get(): Array<AnsweredQuestion> {
32
+ // const answers = this.history.map((h) => h.answeredQuestions).flat(1);
33
+ // return answers;
34
+ // }
35
+
36
+ addToHistory(result: PageResult) {
37
+ this.history.push(result);
38
+ }
39
+ }
@@ -1,19 +1,30 @@
1
1
  import { NextQue } from "./next-que";
2
- import { PageDto } from "../dto/SchemaDto";
3
2
  import { PageID } from "../utils/ID";
3
+ import { PageDto } from "../page/Page";
4
4
 
5
5
  const tag1 = "tag1";
6
6
  const tag2 = "tag2";
7
7
  const tag3 = "tag3";
8
8
 
9
9
  let que = new NextQue();
10
- const createPage = () => {};
11
- const p1: PageDto = { id: PageID.create(), elements: [], tags: [tag1] };
12
- const p2: PageDto = { id: PageID.create(), elements: [], tags: [] };
13
- const p3: PageDto = { id: PageID.create(), elements: [], tags: [tag2] };
14
- const p4: PageDto = { id: PageID.create(), elements: [], tags: [] };
15
- const p5: PageDto = { id: PageID.create(), elements: [], tags: [tag3, tag2] };
16
- const p6: PageDto = { id: PageID.create(), elements: [], tags: [tag3] };
10
+ const createPage = (tags: string[]): PageDto => {
11
+ const dto: PageDto = {
12
+ id: PageID.create(),
13
+ staticElements: [],
14
+ background: "white",
15
+ components: [],
16
+ initialTasks: [],
17
+ tags,
18
+ };
19
+ return dto;
20
+ };
21
+
22
+ const p1 = createPage([tag1]);
23
+ const p2 = createPage([]);
24
+ const p3 = createPage([tag2]);
25
+ const p4 = createPage([]);
26
+ const p5 = createPage([tag3, tag2]);
27
+ const p6 = createPage([tag3]);
17
28
  const all = [p1, p2, p3, p4, p5, p6];
18
29
 
19
30
  beforeEach(() => {
@@ -0,0 +1,93 @@
1
+ import { PageDto } from "../page/Page";
2
+
3
+ export class NextQue {
4
+ private originalOrder: ReadonlyArray<string> = [];
5
+ private allPages: PageDto[] = [];
6
+ private excludedTags = new Set<string>();
7
+ private excludedByPageId = new Set<string>();
8
+ private remaining: PageDto[] = [];
9
+ constructor(pages: PageDto[] = []) {
10
+ this.resetQue(pages);
11
+ }
12
+
13
+ /**
14
+ * Will reset que with the new pages.
15
+ * @param pages
16
+ */
17
+ resetQue(pages: PageDto[]) {
18
+ this.allPages = [...pages];
19
+ this.remaining = [...pages];
20
+ this.excludedTags = new Set();
21
+ this.excludedByPageId = new Set();
22
+ this.originalOrder = this.allPages.map((p) => p.id);
23
+ }
24
+
25
+ pop(): PageDto | false {
26
+ const next = this.remaining.shift();
27
+ // TODO CLONE??
28
+ return next ?? false;
29
+ }
30
+
31
+ peek(): PageDto | false {
32
+ const next = this.remaining[0];
33
+ return next ?? false;
34
+ }
35
+
36
+ jumpToPageById(pageId: string): boolean {
37
+ const index = this.remaining.findIndex((p) => p.id === pageId);
38
+ if (index < 0) {
39
+ return false;
40
+ }
41
+ this.remaining = this.remaining.slice(index);
42
+ return true;
43
+ }
44
+
45
+ removeByTag(tag: string | string[]) {
46
+ if (Array.isArray(tag)) {
47
+ tag.forEach((tag) => {
48
+ this.excludedTags.add(tag);
49
+ });
50
+ } else {
51
+ this.excludedTags.add(tag);
52
+ }
53
+ this.filterRemaining();
54
+ }
55
+
56
+ /**
57
+ * Will not be included
58
+ * @param pages
59
+ */
60
+ insertAsNextByForce(pages: PageDto[]) {
61
+ this.remaining.unshift(...pages);
62
+ }
63
+
64
+ removeByPageId(pageId: string | string[]) {
65
+ if (Array.isArray(pageId)) {
66
+ pageId.forEach((id) => {
67
+ this.excludedByPageId.add(id);
68
+ });
69
+ } else {
70
+ this.excludedByPageId.add(pageId);
71
+ }
72
+ this.filterRemaining();
73
+ // this.excludedByPageId.add(pageId);
74
+ }
75
+
76
+ private filterRemaining() {
77
+ this.remaining = this.remaining.filter((p) => {
78
+ const tags = p.tags ?? [];
79
+ const isIncluededByTag = !tags.some((tag) => this.excludedTags.has(tag));
80
+ const isIncludedByPageId = !this.excludedByPageId.has(p.id);
81
+ return isIncludedByPageId && isIncluededByTag;
82
+ });
83
+ }
84
+ get isEmpty(): boolean {
85
+ return this.remaining.length === 0;
86
+ }
87
+ get size(): number {
88
+ return this.remaining.length;
89
+ }
90
+ get pageCount(): number {
91
+ return this.originalOrder.length;
92
+ }
93
+ }