@monimesl/dot-cli 0.1.0-alpha.3 → 0.1.0-alpha.30

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 (159) hide show
  1. package/README.md +91 -17
  2. package/dist/cli.js +776 -199
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-z7M4FLjf.js → server-DMnWDdJ6.js} +702 -280
  5. package/dist/server-DMnWDdJ6.js.map +1 -0
  6. package/dist/templates/showcase/.env +3 -0
  7. package/dist/templates/showcase/dot.ts +60 -0
  8. package/dist/templates/showcase/pages/basics/bars/controller.ts +47 -0
  9. package/dist/templates/showcase/pages/basics/bars/state.ts +7 -0
  10. package/dist/templates/showcase/pages/basics/bars/view.dml +36 -0
  11. package/dist/templates/showcase/pages/basics/bindings/controller.ts +15 -0
  12. package/dist/templates/showcase/pages/basics/bindings/state.ts +14 -0
  13. package/dist/templates/showcase/pages/basics/bindings/view.dml +41 -0
  14. package/dist/templates/showcase/pages/basics/colors/controller.ts +112 -0
  15. package/dist/templates/showcase/pages/basics/colors/palette.ts +52 -0
  16. package/dist/templates/showcase/pages/basics/colors/state.ts +17 -0
  17. package/dist/templates/showcase/pages/basics/colors/view.dml +142 -0
  18. package/dist/templates/showcase/pages/basics/concert/controller.ts +16 -0
  19. package/dist/templates/showcase/pages/basics/concert/state.ts +7 -0
  20. package/dist/templates/showcase/pages/basics/concert/view.dml +20 -0
  21. package/dist/templates/showcase/pages/basics/control-flow/controller.ts +18 -0
  22. package/dist/templates/showcase/pages/basics/control-flow/state.ts +16 -0
  23. package/dist/templates/showcase/pages/basics/control-flow/view.dml +55 -0
  24. package/dist/templates/showcase/pages/basics/events/controller.ts +43 -0
  25. package/dist/templates/showcase/pages/basics/events/state.ts +11 -0
  26. package/dist/templates/showcase/pages/basics/events/view.dml +51 -0
  27. package/dist/templates/showcase/pages/basics/hooks/controller.ts +43 -0
  28. package/dist/templates/showcase/pages/basics/hooks/state.ts +10 -0
  29. package/dist/templates/showcase/pages/basics/hooks/view.dml +37 -0
  30. package/dist/templates/showcase/pages/basics/navigation/controller.ts +41 -0
  31. package/dist/templates/showcase/pages/basics/navigation/state.ts +11 -0
  32. package/dist/templates/showcase/pages/basics/navigation/view.dml +48 -0
  33. package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +18 -0
  34. package/dist/templates/showcase/pages/basics/pick-genre/state.ts +3 -0
  35. package/dist/templates/showcase/pages/basics/pick-genre/view.dml +10 -0
  36. package/dist/templates/showcase/pages/basics/share/concerts.ts +31 -0
  37. package/dist/templates/showcase/pages/basics/share/controller.ts +12 -0
  38. package/dist/templates/showcase/pages/basics/share/state.ts +5 -0
  39. package/dist/templates/showcase/pages/basics/share/view.dml +21 -0
  40. package/dist/{template/pages → templates/showcase/pages/basics}/state/controller.ts +1 -0
  41. package/dist/{template/pages → templates/showcase/pages/basics}/state/state.ts +1 -1
  42. package/dist/templates/showcase/pages/basics/state/view.dml +46 -0
  43. package/dist/templates/showcase/pages/components/carousel/controller.ts +41 -0
  44. package/dist/templates/showcase/pages/components/carousel/state.ts +77 -0
  45. package/dist/templates/showcase/pages/components/carousel/view.dml +42 -0
  46. package/dist/templates/showcase/pages/components/feedback/controller.ts +32 -0
  47. package/dist/templates/showcase/pages/components/feedback/state.ts +41 -0
  48. package/dist/templates/showcase/pages/components/feedback/view.dml +116 -0
  49. package/dist/templates/showcase/pages/components/forms/controller.ts +72 -0
  50. package/dist/templates/showcase/pages/components/forms/state.ts +37 -0
  51. package/dist/templates/showcase/pages/components/forms/view.dml +97 -0
  52. package/dist/templates/showcase/pages/components/icons/controller.ts +15 -0
  53. package/dist/templates/showcase/pages/components/icons/state.ts +75 -0
  54. package/dist/templates/showcase/pages/components/icons/view.dml +36 -0
  55. package/dist/templates/showcase/pages/components/images/controller.ts +44 -0
  56. package/dist/{template/pages → templates/showcase/pages/components}/images/state.ts +3 -0
  57. package/dist/templates/showcase/pages/components/images/view.dml +83 -0
  58. package/dist/templates/showcase/pages/components/layout/controller.ts +61 -0
  59. package/dist/templates/showcase/pages/components/layout/state.ts +34 -0
  60. package/dist/templates/showcase/pages/components/layout/view.dml +143 -0
  61. package/dist/templates/showcase/pages/components/lists/controller.ts +18 -0
  62. package/dist/templates/showcase/pages/components/lists/state.ts +130 -0
  63. package/dist/templates/showcase/pages/components/lists/view.dml +88 -0
  64. package/dist/templates/showcase/pages/components/money/controller.ts +45 -0
  65. package/dist/templates/showcase/pages/components/money/state.ts +13 -0
  66. package/dist/templates/showcase/pages/components/money/view.dml +54 -0
  67. package/dist/templates/showcase/pages/components/reading/controller.ts +6 -0
  68. package/dist/templates/showcase/pages/components/reading/state.ts +23 -0
  69. package/dist/templates/showcase/pages/components/reading/view.dml +42 -0
  70. package/dist/templates/showcase/pages/components/sheets/controller.ts +36 -0
  71. package/dist/templates/showcase/pages/components/sheets/state.ts +26 -0
  72. package/dist/templates/showcase/pages/components/sheets/view.dml +38 -0
  73. package/dist/templates/showcase/pages/components/styles/controller.ts +6 -0
  74. package/dist/templates/showcase/pages/components/styles/state.ts +3 -0
  75. package/dist/templates/showcase/pages/components/styles/view.dml +101 -0
  76. package/dist/templates/showcase/pages/components/tabs/controller.ts +41 -0
  77. package/dist/templates/showcase/pages/components/tabs/state.ts +127 -0
  78. package/dist/templates/showcase/pages/components/tabs/view.dml +57 -0
  79. package/dist/templates/showcase/pages/components/text/controller.ts +10 -0
  80. package/dist/templates/showcase/pages/components/text/state.ts +3 -0
  81. package/dist/templates/showcase/pages/components/text/view.dml +95 -0
  82. package/dist/templates/showcase/pages/dash/account/controller.ts +32 -0
  83. package/dist/templates/showcase/pages/dash/account/state.ts +9 -0
  84. package/dist/templates/showcase/pages/dash/account/view.dml +33 -0
  85. package/dist/templates/showcase/pages/dash/cache/controller.ts +50 -0
  86. package/dist/templates/showcase/pages/dash/cache/state.ts +17 -0
  87. package/dist/templates/showcase/pages/dash/cache/view.dml +48 -0
  88. package/dist/templates/showcase/pages/dash/calls/controller.ts +67 -0
  89. package/dist/templates/showcase/pages/dash/calls/state.ts +9 -0
  90. package/dist/templates/showcase/pages/dash/calls/view.dml +69 -0
  91. package/dist/templates/showcase/pages/dash/contacts/controller.ts +30 -0
  92. package/dist/templates/showcase/pages/dash/contacts/state.ts +6 -0
  93. package/dist/templates/showcase/pages/dash/contacts/view.dml +33 -0
  94. package/dist/templates/showcase/pages/dash/crypto/controller.ts +38 -0
  95. package/dist/templates/showcase/pages/dash/crypto/state.ts +10 -0
  96. package/dist/templates/showcase/pages/dash/crypto/view.dml +48 -0
  97. package/dist/templates/showcase/pages/dash/files/controller.ts +71 -0
  98. package/dist/templates/showcase/pages/dash/files/state.ts +23 -0
  99. package/dist/templates/showcase/pages/dash/files/view.dml +78 -0
  100. package/dist/templates/showcase/pages/dash/media/controller.ts +65 -0
  101. package/dist/templates/showcase/pages/dash/media/state.ts +12 -0
  102. package/dist/templates/showcase/pages/dash/media/view.dml +53 -0
  103. package/dist/templates/showcase/pages/dash/network/controller.ts +215 -0
  104. package/dist/templates/showcase/pages/dash/network/state.ts +35 -0
  105. package/dist/templates/showcase/pages/dash/network/view.dml +108 -0
  106. package/dist/templates/showcase/pages/dash/secrets/controller.ts +44 -0
  107. package/dist/templates/showcase/pages/dash/secrets/state.ts +5 -0
  108. package/dist/templates/showcase/pages/dash/secrets/view.dml +26 -0
  109. package/dist/templates/showcase/pages/dash/system/controller.ts +89 -0
  110. package/dist/templates/showcase/pages/dash/system/state.ts +13 -0
  111. package/dist/templates/showcase/pages/dash/system/view.dml +96 -0
  112. package/dist/templates/showcase/pages/dash/ui/controller.ts +70 -0
  113. package/dist/templates/showcase/pages/dash/ui/state.ts +8 -0
  114. package/dist/templates/showcase/pages/dash/ui/view.dml +31 -0
  115. package/dist/{template → templates/showcase}/pages/index/controller.ts +1 -1
  116. package/dist/templates/showcase/pages/index/state.ts +237 -0
  117. package/dist/templates/showcase/pages/index/view.dml +23 -0
  118. package/dist/templates/showcase/statics/logo.png +0 -0
  119. package/dist/{template → templates/starter}/.env +1 -1
  120. package/dist/templates/starter/dot.ts +3 -0
  121. package/dist/templates/starter/pages/index/controller.ts +9 -0
  122. package/dist/templates/starter/pages/index/state.ts +4 -0
  123. package/dist/templates/starter/pages/index/view.dml +16 -0
  124. package/package.json +8 -7
  125. package/dist/server-z7M4FLjf.js.map +0 -1
  126. package/dist/template/dot.ts +0 -19
  127. package/dist/template/pages/device/controller.ts +0 -70
  128. package/dist/template/pages/device/state.ts +0 -7
  129. package/dist/template/pages/device/view.dml +0 -44
  130. package/dist/template/pages/feedback/controller.ts +0 -16
  131. package/dist/template/pages/feedback/state.ts +0 -7
  132. package/dist/template/pages/feedback/view.dml +0 -87
  133. package/dist/template/pages/files/controller.ts +0 -47
  134. package/dist/template/pages/files/state.ts +0 -11
  135. package/dist/template/pages/files/view.dml +0 -35
  136. package/dist/template/pages/forms/controller.ts +0 -44
  137. package/dist/template/pages/forms/state.ts +0 -15
  138. package/dist/template/pages/forms/view.dml +0 -39
  139. package/dist/template/pages/images/controller.ts +0 -15
  140. package/dist/template/pages/images/view.dml +0 -38
  141. package/dist/template/pages/index/state.ts +0 -98
  142. package/dist/template/pages/index/view.dml +0 -20
  143. package/dist/template/pages/layout/controller.ts +0 -23
  144. package/dist/template/pages/layout/state.ts +0 -12
  145. package/dist/template/pages/layout/view.dml +0 -63
  146. package/dist/template/pages/lists/controller.ts +0 -36
  147. package/dist/template/pages/lists/state.ts +0 -96
  148. package/dist/template/pages/lists/view.dml +0 -59
  149. package/dist/template/pages/navigation/controller.ts +0 -42
  150. package/dist/template/pages/navigation/state.ts +0 -13
  151. package/dist/template/pages/navigation/view.dml +0 -24
  152. package/dist/template/pages/network/controller.ts +0 -107
  153. package/dist/template/pages/network/state.ts +0 -15
  154. package/dist/template/pages/network/view.dml +0 -56
  155. package/dist/template/pages/state/view.dml +0 -33
  156. package/dist/template/pages/text/controller.ts +0 -10
  157. package/dist/template/pages/text/state.ts +0 -7
  158. package/dist/template/pages/text/view.dml +0 -49
  159. /package/dist/{template → templates/starter}/statics/logo.png +0 -0
@@ -0,0 +1,95 @@
1
+ <View>
2
+ <Text variant="title">Text and links</Text>
3
+ <Text>Text shows words. Its variant sets the size, and the theme sets the font and colours.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">variant</Text>
7
+ <View variant="card" gap="small">
8
+ <Text variant="display" numeric>SLE 1,250</Text>
9
+ <Text variant="title">Palm-wine, a title</Text>
10
+ <Text variant="subtitle">S. E. Rogie, a subtitle</Text>
11
+ <Text variant="lead">A lead opens an article. It's a little bigger than body text.</Text>
12
+ <Text variant="body">Guitar music for a calm evening. This is body text, the default.</Text>
13
+ <Text variant="caption">Recorded in Freetown, in a caption</Text>
14
+ </View>
15
+ </View>
16
+
17
+ <View gap="small">
18
+ <Text variant="subtitle">tone</Text>
19
+ <View variant="card" direction="row" wrap gap="medium">
20
+ <Text tone="default">default</Text>
21
+ <Text tone="muted">muted</Text>
22
+ <Text tone="info">info</Text>
23
+ <Text tone="success">success</Text>
24
+ <Text tone="warning">warning</Text>
25
+ <Text tone="danger">danger</Text>
26
+ </View>
27
+ </View>
28
+
29
+ <View gap="small">
30
+ <Text variant="subtitle">weight, italic and decoration</Text>
31
+ <View variant="card" gap="small">
32
+ <View direction="row" wrap gap="medium">
33
+ <Text weight="regular">regular</Text>
34
+ <Text weight="medium">medium</Text>
35
+ <Text weight="bold">bold</Text>
36
+ <Text italic>italic</Text>
37
+ </View>
38
+ <View direction="row" wrap gap="medium">
39
+ <Text decoration="underline">underline</Text>
40
+ <Text decoration="strikethrough">strikethrough</Text>
41
+ <Text decoration="none">none</Text>
42
+ </View>
43
+ </View>
44
+ </View>
45
+
46
+ <View gap="small">
47
+ <Text variant="subtitle">align and numeric</Text>
48
+ <Text variant="caption" tone="muted">numeric gives every digit the same width, so amounts line up.</Text>
49
+ <View variant="card" gap="small">
50
+ <Text align="start">At the start</Text>
51
+ <Text align="center">In the center</Text>
52
+ <Text align="end">At the end</Text>
53
+ <Text align="end" numeric>SLE 1,111.11</Text>
54
+ <Text align="end" numeric>SLE 8,888.88</Text>
55
+ </View>
56
+ </View>
57
+
58
+ <View gap="small">
59
+ <Text variant="subtitle">maxLines</Text>
60
+ <Text variant="caption" tone="muted">Cuts the text off after this many lines.</Text>
61
+ <View variant="card">
62
+ <Text maxLines="2">Sierra Leone's Refugee All Stars came together in refugee camps in Guinea, recorded their first album, Living Like a Refugee, back in Freetown, and carried Salone music to stages around the world. Two lines are allowed here, so the rest is cut off.</Text>
63
+ </View>
64
+ </View>
65
+
66
+ <View gap="small">
67
+ <Text variant="subtitle">leading and selectable</Text>
68
+ <Text variant="caption" tone="muted">leading moves the lines closer or further apart. selectable lets the person copy the text: hold your finger on the last one.</Text>
69
+ <View variant="card" gap="medium">
70
+ <Text leading="tight">Tight lines. Palm-wine music grew in the bars of Freetown, played on guitar and sung in Krio.</Text>
71
+ <Text leading="normal">Normal lines. Palm-wine music grew in the bars of Freetown, played on guitar and sung in Krio.</Text>
72
+ <Text leading="relaxed" selectable>Relaxed lines, and you can select them. Palm-wine music grew in the bars of Freetown, played on guitar and sung in Krio.</Text>
73
+ </View>
74
+ </View>
75
+
76
+ <View gap="small">
77
+ <Text variant="subtitle">Text inside text</Text>
78
+ <View variant="card">
79
+ <Text>Text can hold <Text weight="bold">bold</Text> and <Text tone="danger">red</Text> words.</Text>
80
+ </View>
81
+ </View>
82
+
83
+ <View gap="small">
84
+ <Text variant="subtitle">Link</Text>
85
+ <Text variant="caption" tone="muted">With href, a link opens a web page in the phone's browser. With onTap, it runs a handler.</Text>
86
+ <View variant="card" gap="small">
87
+ <Text>Read about <Link href="https://en.wikipedia.org/wiki/Music_of_Sierra_Leone">the music of Sierra Leone</Link>.</Text>
88
+ <View direction="row" gap="medium">
89
+ <Link onTap="like">Like the song</Link>
90
+ <Link tone="danger" onTap="unlike">Take it back</Link>
91
+ </View>
92
+ <Text tone="muted">{{ likes }} {{ likes === 1 ? 'like' : 'likes' }}</Text>
93
+ </View>
94
+ </View>
95
+ </View>
@@ -0,0 +1,32 @@
1
+ import { errorMessage } from '@monimesl/dot-runtime';
2
+
3
+ import state from './state';
4
+
5
+ export default definePage({
6
+ initialState: state,
7
+ configs: { navigationBar: { title: 'dash.account' } },
8
+ mutations: {
9
+ setNonce: (_state, event: ValueEvent) => ({ nonce: event.detail.value }),
10
+ },
11
+ actions: {
12
+ async signIn(state) {
13
+ try {
14
+ const { fullName, alias, accountIdToken } = await this.dash.account.signIn({
15
+ mode: 'simple',
16
+ nonce: state.nonce,
17
+ });
18
+ this.setState({ fullName, alias, accountIdToken, note: '' });
19
+ } catch (error) {
20
+ this.setState({ note: errorMessage(error) });
21
+ }
22
+ },
23
+ async signInWithOidc() {
24
+ try {
25
+ const { id } = await this.dash.account.signIn({ mode: 'oidc', scopes: ['profile'] });
26
+ this.setState({ oidcNote: `Signed in as ${id}` });
27
+ } catch (error) {
28
+ this.setState({ oidcNote: errorMessage(error) });
29
+ }
30
+ },
31
+ },
32
+ });
@@ -0,0 +1,9 @@
1
+ export default {
2
+ // A fixed nonce, so the token names the person the same way each time in this dot.
3
+ nonce: 'salone-sounds',
4
+ fullName: '',
5
+ alias: '',
6
+ accountIdToken: '',
7
+ note: '',
8
+ oidcNote: '',
9
+ };
@@ -0,0 +1,33 @@
1
+ <View>
2
+ <Text variant="title">dash.account</Text>
3
+ <Text>Who is signed in to Dash. It needs the account permission in dot.json.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">signIn</Text>
7
+ <Text variant="caption" tone="muted">With mode "simple", Dash says who the person is: their full name, the phone number or email address they use Dash with, and a token Monime makes from this dot's id, their Monimeer id and the nonce, with a key only it knows. The same nonce gives the same token, on any phone. It names the person, but proves nothing to the dot's server.</Text>
8
+ <View variant="card" gap="medium">
9
+ <Input label="Nonce" value="{{ nonce }}" onChange="setNonce" />
10
+ <Button onClick="signIn">Sign in with Dash</Button>
11
+ <If condition="{{ fullName }}">
12
+ <View gap="none">
13
+ <DetailRow label="fullName" value="{{ fullName }}" />
14
+ <DetailRow label="alias" value="{{ alias }}" />
15
+ <DetailRow label="accountIdToken" value="{{ accountIdToken }}" />
16
+ </View>
17
+ </If>
18
+ </View>
19
+ </View>
20
+
21
+ <View gap="small">
22
+ <Text variant="subtitle">signIn with OpenID Connect</Text>
23
+ <Text variant="caption" tone="muted">With mode "oidc", Dash will give an ID token the dot's server checks with Monime's keys, and only the details the person agrees to share. It isn't ready yet, so Dash says so.</Text>
24
+ <Button variant="secondary" onClick="signInWithOidc">Sign in with OpenID Connect</Button>
25
+ <If condition="{{ oidcNote }}">
26
+ <Notice tone="info">{{ oidcNote }}</Notice>
27
+ </If>
28
+ </View>
29
+
30
+ <If condition="{{ note }}">
31
+ <Notice tone="danger">{{ note }}</Notice>
32
+ </If>
33
+ </View>
@@ -0,0 +1,50 @@
1
+ import state from './state';
2
+
3
+ const RECENT = 'recent/';
4
+ const GENRE = 'genre';
5
+ const RECENT_MILLISECONDS = 30_000;
6
+
7
+ export default definePage({
8
+ initialState: state,
9
+ configs: { navigationBar: { title: 'dash.storage.cache' } },
10
+ hooks: {
11
+ async onShow() {
12
+ await this.actions.refresh();
13
+ },
14
+ },
15
+ actions: {
16
+ async refresh() {
17
+ const keys = await this.dash.storage.cache.keys();
18
+ const recent: string[] = [];
19
+ for (const key of keys.filter((each) => each.startsWith(RECENT))) {
20
+ recent.push((await this.dash.storage.cache.get<string>(key)) ?? '');
21
+ }
22
+ const hasGenre = await this.dash.storage.cache.exists(GENRE);
23
+ this.setState({
24
+ recent,
25
+ genre: hasGenre ? ((await this.dash.storage.cache.get<string>(GENRE)) ?? '') : '',
26
+ keys: keys.join(', '),
27
+ });
28
+ },
29
+ // Each view lasts 30 seconds, then the cache forgets it.
30
+ async view(_state, event: DataEvent<{ id: string; name: string }>) {
31
+ await this.dash.storage.cache.set(`${RECENT}${event.data.id}`, event.data.name, {
32
+ ttl: RECENT_MILLISECONDS,
33
+ });
34
+ await this.actions.refresh();
35
+ },
36
+ // With no ttl, the genre lasts until it's removed.
37
+ async setGenre(_state, event: ValueEvent) {
38
+ await this.dash.storage.cache.set(GENRE, event.detail.value);
39
+ await this.actions.refresh();
40
+ },
41
+ async forgetGenre() {
42
+ await this.dash.storage.cache.remove(GENRE);
43
+ await this.actions.refresh();
44
+ },
45
+ async clear() {
46
+ await this.dash.storage.cache.clear();
47
+ await this.actions.refresh();
48
+ },
49
+ },
50
+ });
@@ -0,0 +1,17 @@
1
+ export interface Artist {
2
+ id: string;
3
+ name: string;
4
+ }
5
+
6
+ export default {
7
+ artists: [
8
+ { id: 'drizilik', name: 'Drizilik' },
9
+ { id: 'the-therapist', name: 'The Therapist' },
10
+ { id: 'emmerson', name: 'Emmerson' },
11
+ { id: 'kao-denero', name: 'Kao Denero' },
12
+ ] as Artist[],
13
+ // The artists viewed in the last 30 seconds, as the cache still has them.
14
+ recent: [] as string[],
15
+ genre: '',
16
+ keys: '',
17
+ };
@@ -0,0 +1,48 @@
1
+ <View>
2
+ <Text variant="title">dash.storage.cache</Text>
3
+ <Text>The cache keeps small values by name, such as text, numbers or JSON. A value can expire after a while, or stay until it's removed.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">set with a ttl, keys and get</Text>
7
+ <Text variant="caption" tone="muted">Tap an artist. ttl: 30_000 keeps it for 30 seconds, as times are in milliseconds.</Text>
8
+ <List>
9
+ <For each="{{ artists }}" item="artist" key="id">
10
+ <ListItem icon="microphone" title="{{ artist.name }}" data-id="{{ artist.id }}" data-name="{{ artist.name }}" onTap="view" />
11
+ </For>
12
+ </List>
13
+ <View variant="card" gap="small">
14
+ <If condition="{{ recent.length > 0 }}">
15
+ <For each="{{ recent }}" item="name" key="*this">
16
+ <Text>{{ name }}</Text>
17
+ </For>
18
+ </If>
19
+ <Else>
20
+ <Text variant="caption" tone="muted">No artists viewed in the last 30 seconds</Text>
21
+ </Else>
22
+ <Button variant="secondary" onClick="refresh">Check again</Button>
23
+ </View>
24
+ </View>
25
+
26
+ <View gap="small">
27
+ <Text variant="subtitle">set without a ttl, and exists</Text>
28
+ <Text variant="caption" tone="muted">Your favourite genre stays until it's removed, even after the dot closes.</Text>
29
+ <View variant="card">
30
+ <Select label="Genre" placeholder="Choose a genre" value="{{ genre }}" onChange="setGenre">
31
+ <Option value="Afropop" label="Afropop" />
32
+ <Option value="Palm-wine" label="Palm-wine" />
33
+ <Option value="Gumbe" label="Gumbe" />
34
+ </Select>
35
+ </View>
36
+ </View>
37
+
38
+ <View gap="small">
39
+ <Text variant="subtitle">remove and clear</Text>
40
+ <View variant="card" gap="small">
41
+ <Text>Keys in the cache: {{ keys || 'none' }}</Text>
42
+ <View direction="row" wrap gap="small">
43
+ <Button variant="secondary" onClick="forgetGenre" disabled="{{ !genre }}">Forget the genre</Button>
44
+ <Button variant="secondary" onClick="clear" disabled="{{ !keys }}">Clear the cache</Button>
45
+ </View>
46
+ </View>
47
+ </View>
48
+ </View>
@@ -0,0 +1,67 @@
1
+ import type { DashRequest } from '@monimesl/dot-runtime';
2
+ import { errorMessage, isDashError } from '@monimesl/dot-runtime';
3
+
4
+ import state from './state';
5
+
6
+ // The long wait, kept so the Cancel button can stop it.
7
+ let wait: DashRequest<void> | null = null;
8
+
9
+ export default definePage({
10
+ initialState: state,
11
+ configs: { navigationBar: { title: 'Calls and errors' } },
12
+ hooks: {
13
+ async onLoad() {
14
+ this.setState({ picksPhones: this.dash.supports('contacts.pickPhone') });
15
+ try {
16
+ const { apiVersions } = await this.dash.system.info();
17
+ this.setState({ apiVersions: apiVersions.join(', ') });
18
+ } catch (error) {
19
+ this.setState({ apiVersions: errorMessage(error) });
20
+ }
21
+ },
22
+ },
23
+ actions: {
24
+ // Makes the call a button names, and shows how it ended.
25
+ async show(_state, event: DataEvent<{ call: string }>) {
26
+ this.setState({ code: '', message: '' });
27
+ try {
28
+ switch (event.data.call) {
29
+ case 'timeout':
30
+ // Gives up after 1 second on a wait of 5.
31
+ await this.dash.system.delay(5000, { timeout: 1000 });
32
+ break;
33
+ case 'cancelled':
34
+ wait = this.dash.system.delay(10_000);
35
+ this.setState({ waiting: true });
36
+ await wait;
37
+ break;
38
+ case 'invalid':
39
+ await this.dash.ui.pickDate({ min: '2026-12-31', max: '2026-01-01' });
40
+ break;
41
+ case 'denied':
42
+ await this.dash.network.http.request({ url: 'https://example.com' });
43
+ break;
44
+ case 'missing':
45
+ await this.dash.storage.file.read('never-saved.txt');
46
+ break;
47
+ }
48
+ this.setState({ code: 'none', message: 'It worked' });
49
+ } catch (error) {
50
+ if (isDashError(error)) {
51
+ this.setState({ code: error.code, message: error.message });
52
+ } else {
53
+ this.setState({ code: 'not a DashError', message: errorMessage(error) });
54
+ }
55
+ } finally {
56
+ wait = null;
57
+ this.setState({ waiting: false });
58
+ }
59
+ },
60
+ async closeDot() {
61
+ await this.dash.dot.close();
62
+ },
63
+ cancelWait() {
64
+ wait?.cancel();
65
+ },
66
+ },
67
+ });
@@ -0,0 +1,9 @@
1
+ export default {
2
+ waiting: false,
3
+ // The last call's outcome: the DashError's code and message, or how it ended.
4
+ code: '',
5
+ message: '',
6
+ // What this Dash offers, for the supports section.
7
+ apiVersions: '',
8
+ picksPhones: false,
9
+ };
@@ -0,0 +1,69 @@
1
+ <View>
2
+ <Text variant="title">Calls and errors</Text>
3
+ <Text>Every this.dash call goes to Dash and comes back later, so await it. When it fails, it throws a DashError with a code that says why.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">timeout</Text>
7
+ <Text variant="caption" tone="muted">The last argument of a call can set a timeout, in milliseconds. This waits 5 seconds with a timeout of 1.</Text>
8
+ <Button variant="secondary" data-call="timeout" onClick="show">Wait too long</Button>
9
+ </View>
10
+
11
+ <View gap="small">
12
+ <Text variant="subtitle">cancel</Text>
13
+ <Text variant="caption" tone="muted">A call can be cancelled while it runs. Calls still running when the page closes are cancelled for you.</Text>
14
+ <View direction="row" gap="small">
15
+ <Button variant="secondary" data-call="cancelled" onClick="show" disabled="{{ waiting }}">Wait 10 seconds</Button>
16
+ <Button variant="secondary" onClick="cancelWait" disabled="{{ !waiting }}">Cancel</Button>
17
+ </View>
18
+ </View>
19
+
20
+ <View gap="small">
21
+ <Text variant="subtitle">DashError codes</Text>
22
+ <Text variant="caption" tone="muted">isDashError(error) tells a Dash call's error from any other, and isDashError(error, 'timeout') also checks its code.</Text>
23
+ <View variant="card" gap="none">
24
+ <DetailRow label="cancelled" value="The person closed it, or the call was cancelled" />
25
+ <DetailRow label="timeout" value="It took longer than the timeout" />
26
+ <DetailRow label="denied" value="Not allowed, such as a missing permission" />
27
+ <DetailRow label="invalid" value="The call's options are wrong" />
28
+ <DetailRow label="unsupported" value="This Dash can't do it" />
29
+ <DetailRow label="missing" value="There's nothing there, such as a file that isn't saved" />
30
+ <DetailRow label="failed" value="Anything else went wrong" />
31
+ </View>
32
+ <View direction="row" wrap gap="small">
33
+ <Button variant="secondary" data-call="invalid" onClick="show">Pick a date between bad limits</Button>
34
+ <Button variant="secondary" data-call="denied" onClick="show">Reach an unlisted host</Button>
35
+ <Button variant="secondary" data-call="missing" onClick="show">Read a file never saved</Button>
36
+ </View>
37
+ </View>
38
+
39
+ <View gap="small">
40
+ <Text variant="subtitle">supports</Text>
41
+ <Text variant="caption" tone="muted">Each call's docs say which Dash API version it came in, and dot.json's dash.minApiVersion says which one the dot needs. To use a newer call and still open on older Dash, check this.dash.supports first.</Text>
42
+ <View variant="card" gap="none">
43
+ <DetailRow label="This Dash offers" value="Dash API {{ apiVersions }}" />
44
+ <DetailRow label="contacts.pickPhone" value="{{ picksPhones ? 'Supported' : 'Not on this Dash' }}" />
45
+ </View>
46
+ </View>
47
+
48
+ <View gap="small">
49
+ <Text variant="subtitle">dash.dot.close</Text>
50
+ <Text variant="caption" tone="muted">Closes the dot, as going back from its first page does, such as once a payment is done.</Text>
51
+ <Button variant="secondary" onClick="closeDot">Close the showcase</Button>
52
+ </View>
53
+
54
+ <View gap="small">
55
+ <Text variant="subtitle">The last call</Text>
56
+ <View variant="card" gap="none">
57
+ <If condition="{{ code }}">
58
+ <DetailRow label="error.code" value="{{ code }}" weight="bold" />
59
+ <DetailRow label="error.message" value="{{ message }}" />
60
+ </If>
61
+ <ElseIf condition="{{ waiting }}">
62
+ <Text tone="muted">Waiting…</Text>
63
+ </ElseIf>
64
+ <Else>
65
+ <Text tone="muted">Use a button above</Text>
66
+ </Else>
67
+ </View>
68
+ </View>
69
+ </View>
@@ -0,0 +1,30 @@
1
+ import { errorMessage, unlessCancelled } from '@monimesl/dot-runtime';
2
+
3
+ import state from './state';
4
+
5
+ export default definePage({
6
+ initialState: state,
7
+ configs: { navigationBar: { title: 'dash.contacts' } },
8
+ actions: {
9
+ async choose() {
10
+ try {
11
+ const contact = await unlessCancelled(this.dash.contacts.pickPhone());
12
+ if (contact) {
13
+ this.setState({ contact, note: '' });
14
+ }
15
+ } catch (error) {
16
+ this.setState({ note: errorMessage(error) });
17
+ }
18
+ },
19
+ async call(state) {
20
+ if (!state.contact) {
21
+ return;
22
+ }
23
+ try {
24
+ await this.dash.system.dial(state.contact.phone);
25
+ } catch (error) {
26
+ this.setState({ note: errorMessage(error) });
27
+ }
28
+ },
29
+ },
30
+ });
@@ -0,0 +1,6 @@
1
+ import type { PickedContact } from '@monimesl/dot-runtime';
2
+
3
+ export default {
4
+ contact: null as PickedContact | null,
5
+ note: '',
6
+ };
@@ -0,0 +1,33 @@
1
+ <View>
2
+ <Text variant="title">dash.contacts</Text>
3
+ <Text>The phone's own contacts. The dot sees only the one the person chooses.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">pickPhone</Text>
7
+ <Text variant="caption" tone="muted">The person chooses one phone number, such as who to send money to. It needs the contacts permission in dot.json.</Text>
8
+ <View variant="card" gap="medium">
9
+ <If condition="{{ contact }}">
10
+ <View direction="row" align="center" gap="medium">
11
+ <Avatar name="{{ contact.name }}" />
12
+ <View grow gap="none">
13
+ <Text weight="medium">{{ contact.name || 'No name' }}</Text>
14
+ <Text variant="caption" tone="muted">{{ contact.phone }}</Text>
15
+ </View>
16
+ </View>
17
+ <View direction="row" gap="small">
18
+ <Button variant="secondary" onClick="choose">Choose another</Button>
19
+ <Button variant="secondary" onClick="call">Call</Button>
20
+ </View>
21
+ </If>
22
+ <Else>
23
+ <EmptyState icon="users" title="No one chosen" text="Choose a contact to send money to.">
24
+ <Button onClick="choose">Choose a contact</Button>
25
+ </EmptyState>
26
+ </Else>
27
+ </View>
28
+ </View>
29
+
30
+ <If condition="{{ note }}">
31
+ <Notice tone="danger">{{ note }}</Notice>
32
+ </If>
33
+ </View>
@@ -0,0 +1,38 @@
1
+ import { errorMessage } from '@monimesl/dot-runtime';
2
+
3
+ import state from './state';
4
+
5
+ export default definePage({
6
+ initialState: state,
7
+ configs: { navigationBar: { title: 'dash.crypto' } },
8
+ mutations: {
9
+ setText: (_state, event: ValueEvent) => ({ text: event.detail.value }),
10
+ setKey: (_state, event: ValueEvent) => ({ key: event.detail.value }),
11
+ // dash.utils answers at once, so even a mutation can use it.
12
+ encode: (state) => ({ encoded: dash.utils.base64.encode(state.text) }),
13
+ },
14
+ actions: {
15
+ async hash(state) {
16
+ try {
17
+ const sha256 = await this.dash.crypto.sha256(state.text);
18
+ const hmac = await this.dash.crypto.hmac({
19
+ algorithm: 'sha256',
20
+ key: state.key,
21
+ data: state.text,
22
+ });
23
+ this.setState({ sha256, hmac, note: '' });
24
+ } catch (error) {
25
+ this.setState({ note: errorMessage(error) });
26
+ }
27
+ },
28
+ async makeRandom() {
29
+ try {
30
+ const random = await this.dash.crypto.randomBytes(16);
31
+ const uuid = await this.dash.crypto.randomUUID();
32
+ this.setState({ random, uuid, note: '' });
33
+ } catch (error) {
34
+ this.setState({ note: errorMessage(error) });
35
+ }
36
+ },
37
+ },
38
+ });
@@ -0,0 +1,10 @@
1
+ export default {
2
+ text: 'Salone Sounds Live, Freetown',
3
+ key: 'shared secret',
4
+ sha256: '',
5
+ hmac: '',
6
+ random: '',
7
+ uuid: '',
8
+ encoded: '',
9
+ note: '',
10
+ };
@@ -0,0 +1,48 @@
1
+ <View>
2
+ <Text variant="title">dash.crypto</Text>
3
+ <Text>Hashes, signatures and random values, worked out on the phone. Each gives hex, or base64 with { as: 'base64' }.</Text>
4
+
5
+ <View variant="card" gap="medium">
6
+ <Input label="Text" value="{{ text }}" onChange="setText" />
7
+ <Input label="HMAC key" value="{{ key }}" onChange="setKey" />
8
+ </View>
9
+
10
+ <View gap="small">
11
+ <Text variant="subtitle">sha256 and hmac</Text>
12
+ <Text variant="caption" tone="muted">sha512 works the same way. hmac signs with a key, such as to check a message from the dot's own server.</Text>
13
+ <Button variant="secondary" onClick="hash">Hash and sign the text</Button>
14
+ <If condition="{{ sha256 }}">
15
+ <View variant="card" gap="none">
16
+ <DetailRow label="sha256" value="{{ sha256 }}" />
17
+ <DetailRow label="hmac sha256" value="{{ hmac }}" />
18
+ </View>
19
+ </If>
20
+ </View>
21
+
22
+ <View gap="small">
23
+ <Text variant="subtitle">randomBytes and randomUUID</Text>
24
+ <Text variant="caption" tone="muted">From the phone's secure random source, such as for a nonce.</Text>
25
+ <Button variant="secondary" onClick="makeRandom">Make random values</Button>
26
+ <If condition="{{ uuid }}">
27
+ <View variant="card" gap="none">
28
+ <DetailRow label="16 random bytes" value="{{ random }}" />
29
+ <DetailRow label="randomUUID" value="{{ uuid }}" />
30
+ </View>
31
+ </If>
32
+ </View>
33
+
34
+ <View gap="small">
35
+ <Text variant="subtitle">dash.utils</Text>
36
+ <Text variant="caption" tone="muted">Not Dash calls: dash.utils.base64 and dash.utils.hex run in the dot and answer at once, without await, for UTF-8 text.</Text>
37
+ <Button variant="secondary" onClick="encode">Encode the text</Button>
38
+ <If condition="{{ encoded }}">
39
+ <View variant="card" gap="none">
40
+ <DetailRow label="utils.base64.encode" value="{{ encoded }}" />
41
+ </View>
42
+ </If>
43
+ </View>
44
+
45
+ <If condition="{{ note }}">
46
+ <Notice tone="danger">{{ note }}</Notice>
47
+ </If>
48
+ </View>