1
+ import * as G from 'typings/graphql'
2
+ import * as CR from 'typings'
3
+
4
+ class Tutorial {
5
+ public repo :G . TutorialRepo
6
+ public config :{ codingLanguage :G . EnumCodingLanguage , testRunner :G . EnumTestRunner }
7
+ private version :G . TutorialVersion
8
+ private position :CR . Position
9
+ private progress :CR . Progress
10
+
11
+ constructor ( tutorial :G . Tutorial ) {
12
+ this . repo = tutorial . repo
13
+ this . config = {
14
+ codingLanguage :tutorial . codingLanguage ,
15
+ testRunner :tutorial . testRunner ,
16
+ }
17
+ // TODO: allow specific version, currently defaults to latest
18
+ this . version = tutorial . version
19
+ // set initial position
20
+ this . position = {
21
+ levelId :this . version . levels [ 0 ] . id ,
22
+ stageId :this . version . levels [ 0 ] . stages [ 0 ] . id ,
23
+ stepId :this . version . levels [ 0 ] . stages [ 0 ] . steps [ 0 ] . id ,
24
+ }
25
+ // set empty initial progress
26
+ this . progress = {
27
+ levels :{ } ,
28
+ stages :{ } ,
29
+ steps :{ } ,
30
+ complete :false ,
31
+ }
32
+ }
33
+ public load = ( ) => {
34
+ //
35
+ }
36
+ public level = ( levelId :string ) :G . Level | null => {
37
+ const level :G . Level | undefined = this . version . levels . find ( ( l :G . Level ) => l . id === levelId )
38
+ if ( ! level ) {
39
+ console . warn ( `LevelId not found:${ levelId } ` )
40
+ return null
41
+ }
42
+ return level
43
+ }
44
+ public stage = ( stageId :string ) :G . Stage | null => {
45
+ const level :G . Level | null = this . level ( this . position . levelId )
46
+ if ( ! level ) {
47
+ return null
48
+ }
49
+ const stage :G . Stage | undefined = level . stages . find ( ( s :G . Stage ) => s . id === stageId )
50
+ if ( ! stage ) {
51
+ console . warn ( `StageId not found:${ stageId } ` )
52
+ return null
53
+ }
54
+ return stage
55
+ }
56
+ public step = ( stepId :string ) :G . Step | null => {
57
+ const stage :G . Stage | null = this . stage ( this . position . stageId )
58
+ if ( ! stage ) {
59
+ return null
60
+ }
61
+ const step :G . Step | undefined = stage . steps . find ( ( s :G . Step ) => s . id === stepId )
62
+ if ( ! step ) {
63
+ console . warn ( `StepId not found:${ stepId } ` )
64
+ return null
65
+ }
66
+ return step
67
+ }
68
+ public updateProgress = ( ) :{ position :CR . Position , progress :CR . Progress } => {
69
+ const { levelId, stageId, stepId} = this . position
70
+ this . progress . levels [ levelId ] = true
71
+ this . progress . stages [ stageId ] = true
72
+ this . progress . steps [ stepId ] = true
73
+ return {
74
+ position :this . position ,
75
+ progress :this . progress ,
76
+ }
77
+ }
78
+ public nextPosition = ( ) :CR . Position => {
79
+ const { levelId, stageId, stepId} = this . position
80
+ // TODO: calculate and return next position
81
+
82
+ // is next step
83
+ const stage :G . Stage | null = this . stage ( stageId )
84
+ if ( ! stage ) {
85
+ throw new Error ( 'Stage not found' )
86
+ }
87
+ const { steps} = stage
88
+ const indexOfStep = steps . findIndex ( ( s :G . Step ) :boolean => s . id === stepId )
89
+ if ( indexOfStep === - 1 ) {
90
+ throw new Error ( 'Step not found' )
91
+ }
92
+ if ( indexOfStep < steps . length - 1 ) {
93
+
94
+ return {
95
+ levelId,
96
+ stageId,
97
+ stepId :steps [ indexOfStep + 1 ] . id
98
+ }
99
+ }
100
+
101
+ // is next stage
102
+ const level :G . Level | null = this . level ( levelId )
103
+ if ( ! level ) {
104
+ throw new Error ( 'Level not found' )
105
+ }
106
+ const { stages} = level
107
+ const indexOfStage = stages . findIndex ( ( s :G . Stage ) :boolean => s . id === stageId )
108
+ if ( indexOfStage === - 1 ) {
109
+ throw new Error ( 'Stage not found' )
110
+ }
111
+ if ( indexOfStage < stages . length - 1 ) {
112
+ // next stage
113
+ const nextStage = stages [ indexOfStage + 1 ]
114
+ return {
115
+ levelId,
116
+ stageId :nextStage . id ,
117
+ stepId :nextStage . steps [ 0 ] . id
118
+ }
119
+ }
120
+
121
+ // is next level
122
+ const levels = this . version . levels
123
+ const indexOfLevel = levels . findIndex ( ( l :G . Level ) :boolean => l . id === levelId )
124
+ if ( indexOfLevel === - 1 ) {
125
+ throw new Error ( 'Level not found' )
126
+ }
127
+ if ( indexOfLevel < levels . length - 1 ) {
128
+ const nextLevel = levels [ indexOfLevel + 1 ]
129
+ const nextStage = nextLevel . stages [ 0 ]
130
+ return {
131
+ levelId :nextLevel . id ,
132
+ stageId :nextStage . id ,
133
+ stepId :nextStage . steps [ 0 ] . id ,
134
+ }
135
+ }
136
+
137
+ throw new Error ( 'Could not calculate next position' )
138
+ }
139
+ }
140
+
141
+ export default Tutorial