@monimesl/dot-cli 0.1.0-alpha.7 → 0.1.0-alpha.9

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 (57) hide show
  1. package/README.md +17 -9
  2. package/dist/cli.js +158 -71
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-DFOUQnLA.js → server-BfPAnsSf.js} +34 -81
  5. package/dist/{server-DFOUQnLA.js.map → server-BfPAnsSf.js.map} +1 -1
  6. package/dist/templates/showcase/.env +1 -1
  7. package/dist/templates/showcase/dot.ts +2 -0
  8. package/dist/templates/showcase/pages/cache/controller.ts +45 -0
  9. package/dist/templates/showcase/pages/cache/state.ts +17 -0
  10. package/dist/templates/showcase/pages/cache/view.dml +36 -0
  11. package/dist/templates/showcase/pages/carousel/controller.ts +33 -3
  12. package/dist/templates/showcase/pages/carousel/state.ts +61 -24
  13. package/dist/templates/showcase/pages/carousel/view.dml +18 -15
  14. package/dist/templates/showcase/pages/device/controller.ts +22 -12
  15. package/dist/templates/showcase/pages/device/state.ts +1 -1
  16. package/dist/templates/showcase/pages/device/view.dml +13 -8
  17. package/dist/templates/showcase/pages/feedback/controller.ts +6 -1
  18. package/dist/templates/showcase/pages/feedback/state.ts +1 -0
  19. package/dist/templates/showcase/pages/feedback/view.dml +23 -23
  20. package/dist/templates/showcase/pages/files/controller.ts +33 -14
  21. package/dist/templates/showcase/pages/files/state.ts +10 -2
  22. package/dist/templates/showcase/pages/files/view.dml +29 -14
  23. package/dist/templates/showcase/pages/filters/controller.ts +11 -12
  24. package/dist/templates/showcase/pages/filters/state.ts +107 -61
  25. package/dist/templates/showcase/pages/filters/view.dml +18 -17
  26. package/dist/templates/showcase/pages/forms/controller.ts +36 -12
  27. package/dist/templates/showcase/pages/forms/state.ts +25 -6
  28. package/dist/templates/showcase/pages/forms/view.dml +28 -18
  29. package/dist/templates/showcase/pages/images/controller.ts +26 -0
  30. package/dist/templates/showcase/pages/images/state.ts +3 -0
  31. package/dist/templates/showcase/pages/images/view.dml +18 -3
  32. package/dist/templates/showcase/pages/index/state.ts +26 -14
  33. package/dist/templates/showcase/pages/index/view.dml +2 -2
  34. package/dist/templates/showcase/pages/layout/controller.ts +36 -2
  35. package/dist/templates/showcase/pages/layout/state.ts +22 -2
  36. package/dist/templates/showcase/pages/layout/view.dml +45 -22
  37. package/dist/templates/showcase/pages/lists/controller.ts +12 -11
  38. package/dist/templates/showcase/pages/lists/state.ts +87 -66
  39. package/dist/templates/showcase/pages/lists/view.dml +15 -15
  40. package/dist/templates/showcase/pages/navigation/controller.ts +2 -1
  41. package/dist/templates/showcase/pages/navigation/view.dml +5 -5
  42. package/dist/templates/showcase/pages/network/controller.ts +68 -24
  43. package/dist/templates/showcase/pages/network/state.ts +13 -6
  44. package/dist/templates/showcase/pages/network/view.dml +29 -18
  45. package/dist/templates/showcase/pages/payments/controller.ts +16 -12
  46. package/dist/templates/showcase/pages/payments/state.ts +3 -0
  47. package/dist/templates/showcase/pages/payments/view.dml +22 -12
  48. package/dist/templates/showcase/pages/secrets/controller.ts +43 -0
  49. package/dist/templates/showcase/pages/secrets/state.ts +5 -0
  50. package/dist/templates/showcase/pages/secrets/view.dml +24 -0
  51. package/dist/templates/showcase/pages/state/state.ts +1 -1
  52. package/dist/templates/showcase/pages/state/view.dml +9 -9
  53. package/dist/templates/showcase/pages/text/controller.ts +3 -3
  54. package/dist/templates/showcase/pages/text/state.ts +6 -5
  55. package/dist/templates/showcase/pages/text/view.dml +22 -13
  56. package/dist/templates/showcase/statics/logo.png +0 -0
  57. package/package.json +6 -6
@@ -1,3 +1,3 @@
1
1
  # Public configuration for the dot. Anyone with the dot can read these variables: never put a
2
2
  # secret here. Read them in the service as `env.GREETING`.
3
- GREETING=This message comes from the dot's .env file.
3
+ GREETING=Welcome to Salone Sounds. This message comes from the dot's .env file.
@@ -17,6 +17,8 @@ export default defineDot({
17
17
  'device',
18
18
  'network',
19
19
  'files',
20
+ 'cache',
21
+ 'secrets',
20
22
  ],
21
23
  plugins: [logger()],
22
24
  });
@@ -0,0 +1,45 @@
1
+ import state from './state';
2
+
3
+ const RECENT = 'recent/';
4
+ const GENRE = 'genre';
5
+ const RECENT_SECONDS = 30;
6
+
7
+ export default definePage({
8
+ initialState: state,
9
+ hooks: {
10
+ async onShow() {
11
+ await this.actions.refresh();
12
+ },
13
+ },
14
+ actions: {
15
+ async refresh() {
16
+ const keys = await this.dash.storage.cache.keys();
17
+ const recent: string[] = [];
18
+ for (const key of keys.filter((each) => each.startsWith(RECENT))) {
19
+ recent.push((await this.dash.storage.cache.get<string>(key)) ?? '');
20
+ }
21
+ const hasGenre = await this.dash.storage.cache.exists(GENRE);
22
+ this.setState({
23
+ recent,
24
+ genre: hasGenre ? ((await this.dash.storage.cache.get<string>(GENRE)) ?? '') : '',
25
+ keys: keys.join(', '),
26
+ });
27
+ },
28
+ // Each view lasts 30 seconds, then the cache forgets it.
29
+ async view(_state, event: DotEvent<unknown, { id: string; name: string }>) {
30
+ await this.dash.storage.cache.set(`${RECENT}${event.data.id}`, event.data.name, {
31
+ ttl: RECENT_SECONDS,
32
+ });
33
+ await this.actions.refresh();
34
+ },
35
+ // With no ttl, the genre lasts until it's removed.
36
+ async setGenre(_state, event: DotEvent<{ value: string }>) {
37
+ await this.dash.storage.cache.set(GENRE, event.detail.value);
38
+ await this.actions.refresh();
39
+ },
40
+ async clear() {
41
+ await this.dash.storage.cache.clear();
42
+ await this.actions.refresh();
43
+ },
44
+ },
45
+ });
@@ -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,36 @@
1
+ <View>
2
+ <Text variant="title">Cache</Text>
3
+ <Text>The cache saves small values by name. A value can expire after a set time, or stay until it is removed.</Text>
4
+
5
+ <Text variant="caption" tone="muted">Tap an artist. It is remembered for 30 seconds.</Text>
6
+ <List>
7
+ <For each="{{ artists }}" item="artist" key="id">
8
+ <ListItem icon="microphone" title="{{ artist.name }}" data-id="{{ artist.id }}" data-name="{{ artist.name }}" onTap="view" />
9
+ </For>
10
+ </List>
11
+ <View variant="card" gap="small">
12
+ <If condition="{{ recent.length > 0 }}">
13
+ <For each="{{ recent }}" item="name" key="name">
14
+ <Text>{{ name }}</Text>
15
+ </For>
16
+ </If>
17
+ <Else>
18
+ <Text variant="caption" tone="muted">No artists viewed in the last 30 seconds</Text>
19
+ </Else>
20
+ <Button variant="secondary" onClick="refresh">Check again</Button>
21
+ </View>
22
+
23
+ <Text variant="caption" tone="muted">Your favourite genre. It stays until you clear the cache.</Text>
24
+ <View variant="card">
25
+ <Select label="Genre" placeholder="Choose a genre" value="{{ genre }}" onChange="setGenre">
26
+ <Option value="Afropop" label="Afropop" />
27
+ <Option value="Palm-wine" label="Palm-wine" />
28
+ <Option value="Gumbe" label="Gumbe" />
29
+ </Select>
30
+ </View>
31
+
32
+ <View variant="card" gap="small">
33
+ <Text variant="caption" tone="muted">Keys in the cache: {{ keys || 'none' }}</Text>
34
+ <Button variant="secondary" onClick="clear" disabled="{{ !keys }}">Clear the cache</Button>
35
+ </View>
36
+ </View>
@@ -1,10 +1,40 @@
1
1
  import state from './state';
2
2
 
3
+ interface Lookup {
4
+ results: { collectionId: number; artworkUrl100: string }[];
5
+ }
6
+
3
7
  export default definePage({
4
8
  initialState: state,
9
+ hooks: {
10
+ // Each artist's photo is the cover of one of their releases, from Apple's catalog.
11
+ async onLoad() {
12
+ const ids = this.state.artists.map((artist) => artist.release).join(',');
13
+ try {
14
+ const { body } = await this.dash.network.http.request<Lookup>({
15
+ url: `https://itunes.apple.com/lookup?id=${ids}`,
16
+ as: 'json',
17
+ });
18
+ const covers = new Map(
19
+ body.results.map((result) => [
20
+ result.collectionId,
21
+ result.artworkUrl100.replace('100x100bb', '600x600bb'),
22
+ ]),
23
+ );
24
+ this.setState((current) => ({
25
+ artists: current.artists.map((artist) => ({
26
+ ...artist,
27
+ photo: covers.get(artist.release) ?? '',
28
+ })),
29
+ }));
30
+ } catch {
31
+ // Without the catalog, the slides go on without photos.
32
+ }
33
+ },
34
+ },
5
35
  mutations: {
6
- showTip: (_state, event: DotEvent<{ index: number }>) => ({ tip: event.detail.index }),
7
- back: (state) => ({ tip: Math.max(state.tip - 1, 0) }),
8
- next: (state) => ({ tip: Math.min(state.tip + 1, state.tips.length - 1) }),
36
+ showChapter: (_state, event: DotEvent<{ index: number }>) => ({ chapter: event.detail.index }),
37
+ back: (state) => ({ chapter: Math.max(state.chapter - 1, 0) }),
38
+ next: (state) => ({ chapter: Math.min(state.chapter + 1, state.chapters.length - 1) }),
9
39
  },
10
40
  });
@@ -1,40 +1,77 @@
1
- export interface Promo {
1
+ export interface Artist {
2
2
  id: string;
3
- title: string;
3
+ name: string;
4
4
  text: string;
5
- icon: string;
5
+ // A release whose cover shows the artist, by its id in Apple's catalog.
6
+ release: number;
7
+ photo: string;
6
8
  }
7
9
 
8
- export interface Tip {
10
+ export interface Chapter {
9
11
  title: string;
10
12
  text: string;
11
13
  }
12
14
 
13
15
  export default {
14
- promos: [
16
+ // Gen Z, the new school.
17
+ artists: [
18
+ {
19
+ id: 'drizilik',
20
+ name: 'Drizilik',
21
+ text: 'Kriofusion rapped in Krio. "Lemonade", from Wait and See, made Rolling Stone\'s best Afropop of 2025.',
22
+ release: 1788321371,
23
+ photo: '',
24
+ },
25
+ {
26
+ id: 'the-therapist',
27
+ name: 'The Therapist',
28
+ text: 'Had two million TikTok followers before his first single, "Nack", in 2022.',
29
+ release: 1612124616,
30
+ photo: '',
31
+ },
32
+ {
33
+ id: 'boii',
34
+ name: 'Boii',
35
+ text: "Spotify's most-streamed Sierra Leonean woman of 2024. Her first EP, Money or Honey, came in 2025.",
36
+ release: 1776449163,
37
+ photo: '',
38
+ },
39
+ {
40
+ id: 'apreel',
41
+ name: 'ApreeL',
42
+ text: '"O\'way", and "Control", a Krio love song that made Rolling Stone\'s best Afropop of 2025.',
43
+ release: 1831185667,
44
+ photo: '',
45
+ },
46
+ {
47
+ id: 'donii-major',
48
+ name: 'Donii Major',
49
+ text: 'R&B from Freetown, from "Hannah" in 2021 to "Shadow" in 2026.',
50
+ release: 6770467984,
51
+ photo: '',
52
+ },
53
+ ] as Artist[],
54
+ chapters: [
55
+ {
56
+ title: 'Palm-wine and gumbe',
57
+ text: 'Ebenezer Calendar & His Maringa Band made Creole palm-wine and gumbe popular, and S. E. Rogie sang palm-wine in four languages.',
58
+ },
59
+ {
60
+ title: 'Milo jazz',
61
+ text: 'Dr. Oloh led Milo Jazz, a street-band sound grown out of gumbe.',
62
+ },
15
63
  {
16
- id: 'send',
17
- title: 'Send money home',
18
- text: 'Free transfers to any mobile wallet this week',
19
- icon: 'send',
64
+ title: 'Afropop',
65
+ text: 'Bunny Mack\'s "Let Me Love You" was a disco hit in 1981, and Emmerson\'s Borbor Bele sang against corruption in 2004.',
20
66
  },
21
67
  {
22
- id: 'bills',
23
- title: 'Pay bills in seconds',
24
- text: 'Electricity, water and airtime in one place',
25
- icon: 'bolt',
68
+ title: 'Salone hip hop',
69
+ text: 'Rapped mostly in Krio, it rose after the civil war with Daddy Saj, K-Man, LAJ, Kao Denero and Star Zee.',
26
70
  },
27
71
  {
28
- id: 'save',
29
- title: 'Save a little each day',
30
- text: 'Set a goal and watch it grow',
31
- icon: 'wallet',
72
+ title: 'Gen Z',
73
+ text: 'Drizilik, Boii and ApreeL sing in Krio over Afrobeats, even amapiano, and new stars break through on Audiomack and TikTok.',
32
74
  },
33
- ] as Promo[],
34
- tips: [
35
- { title: 'Welcome', text: 'Swipe, tap a dot, or use the buttons below.' },
36
- { title: 'The page decides', text: "The carousel's index comes from state." },
37
- { title: 'And hears back', text: 'A swipe raises change, which sets the index again.' },
38
- ] as Tip[],
39
- tip: 0,
75
+ ] as Chapter[],
76
+ chapter: 0,
40
77
  };
@@ -1,26 +1,29 @@
1
1
  <View>
2
2
  <Text variant="title">Carousel</Text>
3
- <Text>A Carousel shows its children one at a time. Swipe between them, or tap a dot.</Text>
3
+ <Text>A carousel shows one slide at a time. Swipe, or tap a dot.</Text>
4
4
 
5
5
  <View gap="small">
6
- <Text variant="caption" tone="muted">Plays by itself and loops, pausing while touched, with a gap between slides that scale in as they come</Text>
6
+ <Text variant="caption" tone="muted">Gen Z artists. The slides move on their own, start over at the end, and pause when you touch them.</Text>
7
7
  <Carousel autoplay loop interval="3000" gap="medium" effect="scale">
8
- <For each="{{ promos }}" item="promo" key="id">
9
- <View variant="card" direction="row" align="center" gap="large">
10
- <Icon name="{{ promo.icon }}" size="large" tone="info" />
11
- <View gap="small" grow>
12
- <Text weight="bold">{{ promo.title }}</Text>
13
- <Text tone="muted">{{ promo.text }}</Text>
14
- </View>
8
+ <For each="{{ artists }}" item="artist" key="id">
9
+ <View variant="card" align="center" gap="medium">
10
+ <If condition="{{ artist.photo }}">
11
+ <Image src="{{ artist.photo }}" alt="{{ artist.name }}" aspect="1:1" />
12
+ </If>
13
+ <Else>
14
+ <Skeleton shape="box" height="240" />
15
+ </Else>
16
+ <Text weight="bold">{{ artist.name }}</Text>
17
+ <Text tone="muted" align="center">{{ artist.text }}</Text>
15
18
  </View>
16
19
  </For>
17
20
  </Carousel>
18
21
  </View>
19
22
 
20
23
  <View gap="small">
21
- <Text variant="caption" tone="muted">The page chooses the slide, and hears when a swipe changes it</Text>
22
- <Carousel index="{{ tip }}" onChange="showTip">
23
- <For each="{{ tips }}" item="card" key="title">
24
+ <Text variant="caption" tone="muted">How Salone music grew, from palm-wine to Gen Z. The buttons move the slides, so there are no dots.</Text>
25
+ <Carousel index="{{ chapter }}" indicator="none" onChange="showChapter">
26
+ <For each="{{ chapters }}" item="card" key="title">
24
27
  <View variant="card" align="center" gap="medium">
25
28
  <Image src="statics/logo.png" alt="" width="48" height="48" />
26
29
  <Text weight="bold">{{ card.title }}</Text>
@@ -29,9 +32,9 @@
29
32
  </For>
30
33
  </Carousel>
31
34
  <View direction="row" gap="small">
32
- <Button variant="secondary" onClick="back" disabled="{{ tip === 0 }}">Back</Button>
33
- <Button onClick="next" disabled="{{ tip === tips.length - 1 }}">Next</Button>
35
+ <Button variant="secondary" onClick="back" disabled="{{ chapter === 0 }}">Back</Button>
36
+ <Button onClick="next" disabled="{{ chapter === chapters.length - 1 }}">Next</Button>
34
37
  </View>
35
- <Text>Tip {{ tip + 1 }} of {{ tips.length }}</Text>
38
+ <Text>Chapter {{ chapter + 1 }} of {{ chapters.length }}</Text>
36
39
  </View>
37
40
  </View>
@@ -9,10 +9,10 @@ export default definePage({
9
9
  async greet() {
10
10
  try {
11
11
  const { action } = await this.dash.ui.alert({
12
- title: 'Hello',
12
+ title: 'Kushé',
13
13
  message: env.GREETING,
14
14
  actions: [
15
- { id: 'nice', label: 'Nice' },
15
+ { id: 'tenki', label: 'Tenki' },
16
16
  { id: 'close', label: 'Close', role: 'cancel' },
17
17
  ],
18
18
  });
@@ -24,11 +24,11 @@ export default definePage({
24
24
  async confirmDelete() {
25
25
  try {
26
26
  const { action } = await this.dash.ui.alert({
27
- title: 'Delete everything?',
28
- message: "This can't be undone.",
27
+ title: 'Remove this song from your library?',
28
+ message: 'Its download goes too.',
29
29
  actions: [
30
30
  { id: 'keep', label: 'Keep', role: 'cancel' },
31
- { id: 'delete', label: 'Delete', role: 'destructive' },
31
+ { id: 'remove', label: 'Remove', role: 'destructive' },
32
32
  ],
33
33
  });
34
34
  this.setState({ choice: action });
@@ -39,23 +39,33 @@ export default definePage({
39
39
  async vibrate(_state, event: DotEvent<unknown, { style: 'light' | 'success' | 'error' }>) {
40
40
  await this.dash.system.vibrate({ style: event.data.style });
41
41
  },
42
- async copyAccount() {
43
- await this.dash.system.copyText('AC-2041');
44
- this.setState({ toast: 'Account number copied' });
42
+ async copySong() {
43
+ await this.dash.system.copyText('Exodus, by Drizilik');
44
+ this.setState({ toast: 'Song copied' });
45
45
  },
46
46
  async shareLink() {
47
47
  try {
48
- await this.dash.system.share({ title: 'Dot', url: 'https://github.com/monime-lab/dot' });
48
+ await this.dash.system.share({
49
+ title: 'Music of Sierra Leone',
50
+ url: 'https://en.wikipedia.org/wiki/Music_of_Sierra_Leone',
51
+ });
52
+ } catch (error) {
53
+ this.setState({ note: (error as Error).message });
54
+ }
55
+ },
56
+ async openDrizilik() {
57
+ try {
58
+ await this.dash.system.openUrl('https://drizilik.com/');
49
59
  } catch (error) {
50
60
  this.setState({ note: (error as Error).message });
51
61
  }
52
62
  },
53
- async sendCode() {
63
+ async countDown() {
54
64
  for (let seconds = 10; seconds > 0; seconds--) {
55
- this.setState({ resendIn: seconds });
65
+ this.setState({ showIn: seconds });
56
66
  await this.dash.system.delay(1000);
57
67
  }
58
- this.setState({ resendIn: 0 });
68
+ this.setState({ showIn: 0, toast: "It's showtime" });
59
69
  },
60
70
  async toggleTorch(state) {
61
71
  const next = state.torch === 'on' ? 'off' : 'on';
@@ -3,5 +3,5 @@ export default {
3
3
  torch: 'off' as 'on' | 'off',
4
4
  note: '',
5
5
  toast: '',
6
- resendIn: 0,
6
+ showIn: 0,
7
7
  };
@@ -1,17 +1,17 @@
1
1
  <View>
2
2
  <Text variant="title">Alerts and device</Text>
3
- <Text>Dash draws these, not the dot: a dot calls them through the global dash object.</Text>
3
+ <Text>Dash shows these, not the dot. The dot asks for them through dash.</Text>
4
4
 
5
5
  <Text variant="caption" tone="muted">dash.ui.alert</Text>
6
6
  <View variant="card" gap="small">
7
7
  <View direction="row" wrap gap="small">
8
- <Button onClick="greet">Say hello</Button>
9
- <Button variant="secondary" onClick="confirmDelete">Ask to delete</Button>
8
+ <Button onClick="greet">Say kushé</Button>
9
+ <Button variant="secondary" onClick="confirmDelete">Remove a song</Button>
10
10
  </View>
11
11
  <If condition="{{ choice }}">
12
12
  <Text>You chose "{{ choice }}"</Text>
13
13
  </If>
14
- <Text variant="caption" tone="muted">The hello message is GREETING in the dot's .env file.</Text>
14
+ <Text variant="caption" tone="muted">Kushé means hello in Krio, and tenki means thanks. The message comes from GREETING in the .env file.</Text>
15
15
  </View>
16
16
 
17
17
  <Text variant="caption" tone="muted">dash.system.vibrate</Text>
@@ -23,18 +23,23 @@
23
23
 
24
24
  <Text variant="caption" tone="muted">dash.system.copyText and dash.system.share</Text>
25
25
  <View variant="card" direction="row" wrap gap="small">
26
- <Button variant="secondary" onClick="copyAccount">Copy AC-2041</Button>
27
- <Button variant="secondary" onClick="shareLink">Share a link</Button>
26
+ <Button variant="secondary" onClick="copySong">Copy a song</Button>
27
+ <Button variant="secondary" onClick="shareLink">Share Sierra Leone's music</Button>
28
+ </View>
29
+
30
+ <Text variant="caption" tone="muted">dash.system.openUrl opens a web page in the phone's browser</Text>
31
+ <View variant="card">
32
+ <Button variant="secondary" onClick="openDrizilik">Open Drizilik's website</Button>
28
33
  </View>
29
34
 
30
35
  <Text variant="caption" tone="muted">dash.system.delay</Text>
31
36
  <View variant="card">
32
- <Button variant="secondary" onClick="sendCode" disabled="{{ resendIn > 0 }}">{{ resendIn > 0 ? 'Resend in ' + resendIn + 's' : 'Send a code' }}</Button>
37
+ <Button variant="secondary" onClick="countDown" disabled="{{ showIn > 0 }}">{{ showIn > 0 ? 'Showtime in ' + showIn + 's' : 'Count down to showtime' }}</Button>
33
38
  </View>
34
39
 
35
40
  <Text variant="caption" tone="muted">dash.system.torch</Text>
36
41
  <View variant="card" gap="small">
37
- <Button variant="secondary" onClick="toggleTorch">{{ torch === 'on' ? 'Turn the torch off' : 'Turn the torch on' }}</Button>
42
+ <Button variant="secondary" onClick="toggleTorch">{{ torch === 'on' ? 'Turn the torch off' : 'Lights up for the slow song' }}</Button>
38
43
  <If condition="{{ note }}">
39
44
  <Text variant="caption" tone="muted">{{ note }}</Text>
40
45
  </If>
@@ -7,9 +7,14 @@ export default definePage({
7
7
  restart: () => ({ progress: 0 }),
8
8
  toggleBusy: (state) => ({ busy: !state.busy }),
9
9
  toggleLoaded: (state) => ({ loaded: !state.loaded }),
10
- showToast: (_state, event: DotEvent<unknown, { message: string; tone: string }>) => ({
10
+ // A toast lasts its default time unless the button gives one, in milliseconds.
11
+ showToast: (
12
+ _state,
13
+ event: DotEvent<unknown, { message: string; tone: string; duration?: string }>,
14
+ ) => ({
11
15
  toast: event.data.message,
12
16
  toastTone: event.data.tone,
17
+ toastDuration: event.data.duration ? Number(event.data.duration) : undefined,
13
18
  }),
14
19
  clearToast: () => ({ toast: '' }),
15
20
  },
@@ -4,4 +4,5 @@ export default {
4
4
  loaded: false,
5
5
  toast: '',
6
6
  toastTone: 'default',
7
+ toastDuration: undefined as number | undefined,
7
8
  };
@@ -1,6 +1,6 @@
1
1
  <View>
2
2
  <Text variant="title">Feedback</Text>
3
- <Text>Show that something is happening, how far along it is, and how it went.</Text>
3
+ <Text>Show that something is loading, how far it has gone, and how it ended.</Text>
4
4
 
5
5
  <View gap="small">
6
6
  <Text variant="caption" tone="muted">Spinners, while something loads</Text>
@@ -12,14 +12,14 @@
12
12
  </View>
13
13
 
14
14
  <View gap="small">
15
- <Text variant="caption" tone="muted">Skeletons, in the shape of what's loading</Text>
15
+ <Text variant="caption" tone="muted">Skeletons show the shape of what is loading</Text>
16
16
  <View variant="card">
17
17
  <If condition="{{ loaded }}">
18
18
  <View direction="row" align="center" gap="large">
19
19
  <Image src="statics/logo.png" alt="Logo" width="40" height="40" shape="circle" />
20
20
  <View gap="none">
21
- <Text weight="medium">Dot Showcase</Text>
22
- <Text variant="caption" tone="muted">Loaded, in place of the skeleton</Text>
21
+ <Text weight="medium">Salone Sounds Radio</Text>
22
+ <Text variant="caption" tone="muted">The loaded content, in place of the skeleton</Text>
23
23
  </View>
24
24
  </View>
25
25
  <Image src="statics/logo.png" alt="Banner" width="320" height="80" fit="contain" />
@@ -36,18 +36,18 @@
36
36
  </View>
37
37
 
38
38
  <View gap="small">
39
- <Text variant="caption" tone="muted">Toasts, which go away on their own</Text>
39
+ <Text variant="caption" tone="muted">Toasts disappear on their own. The offline one stays for 6 seconds.</Text>
40
40
  <View direction="row" gap="small">
41
- <Button variant="secondary" data-message="Saved" data-tone="success" onClick="showToast">Saved</Button>
42
- <Button variant="secondary" data-message="You're offline" data-tone="warning" onClick="showToast">Offline</Button>
41
+ <Button variant="secondary" data-message="Added to your library" data-tone="success" onClick="showToast">Add a song</Button>
42
+ <Button variant="secondary" data-message="You're offline. Only downloads will play." data-tone="warning" data-duration="6000" onClick="showToast">Go offline</Button>
43
43
  </View>
44
44
  </View>
45
45
 
46
46
  <View gap="small">
47
- <Text variant="caption" tone="muted">A progress bar, from 0 to 100</Text>
47
+ <Text variant="caption" tone="muted">A progress bar for an album download, from 0 to 100</Text>
48
48
  <View variant="card">
49
49
  <ProgressBar value="{{ progress }}" />
50
- <Text>{{ progress }}% done</Text>
50
+ <Text>{{ progress }}% downloaded</Text>
51
51
  <View direction="row" gap="small">
52
52
  <Button variant="secondary" onClick="advance" disabled="{{ progress >= 100 }}">Advance</Button>
53
53
  <Button variant="secondary" onClick="restart" disabled="{{ progress === 0 }}">Restart</Button>
@@ -56,32 +56,32 @@
56
56
  </View>
57
57
 
58
58
  <View gap="small">
59
- <Text variant="caption" tone="muted">A button that's busy ignores taps</Text>
59
+ <Text variant="caption" tone="muted">A busy button ignores taps</Text>
60
60
  <View variant="card">
61
- <Button loading="{{ busy }}">{{ busy ? 'Paying…' : 'Pay Le 50,000' }}</Button>
61
+ <Button loading="{{ busy }}">{{ busy ? 'Paying…' : 'Pay Le 300 for two tickets' }}</Button>
62
62
  <Switch label="Busy" checked="{{ busy }}" onChange="toggleBusy" />
63
63
  </View>
64
64
  </View>
65
65
 
66
66
  <View gap="small">
67
- <Text variant="caption" tone="muted">Notices, in the flow of the page</Text>
68
- <Notice title="Heads up">Your meter number is on the bill.</Notice>
69
- <Notice tone="info" title="New">You can now pay water bills.</Notice>
70
- <Notice tone="success" title="Paid">Le 50,000 for electricity.</Notice>
71
- <Notice tone="warning" title="Almost out">Your wallet has Le 2,000 left.</Notice>
67
+ <Text variant="caption" tone="muted">Notices sit inside the page</Text>
68
+ <Notice title="Heads up">Doors open at 7 pm, and the music starts at 8.</Notice>
69
+ <Notice tone="info" title="New">Tickets for gospel night in Kenema are on sale.</Notice>
70
+ <Notice tone="success" title="Booked">Two tickets for Salone Sounds Live.</Notice>
71
+ <Notice tone="warning" title="Almost sold out">Twelve VIP tickets are left.</Notice>
72
72
  <Notice tone="danger" title="Payment failed">The bank didn't answer. Try again.</Notice>
73
73
  </View>
74
74
 
75
75
  <View gap="small">
76
- <Text variant="caption" tone="muted">Badges, for a status</Text>
76
+ <Text variant="caption" tone="muted">Badges show a status</Text>
77
77
  <View direction="row" wrap gap="small">
78
- <Badge>Draft</Badge>
79
- <Badge tone="info">New</Badge>
80
- <Badge tone="success">Paid</Badge>
81
- <Badge tone="warning">Pending</Badge>
82
- <Badge tone="danger">Failed</Badge>
78
+ <Badge>Demo</Badge>
79
+ <Badge tone="info">New release</Badge>
80
+ <Badge tone="success">Booked</Badge>
81
+ <Badge tone="warning">Few left</Badge>
82
+ <Badge tone="danger">Sold out</Badge>
83
83
  </View>
84
84
  </View>
85
85
 
86
- <Toast message="{{ toast }}" tone="{{ toastTone }}" onHide="clearToast" />
86
+ <Toast message="{{ toast }}" tone="{{ toastTone }}" duration="{{ toastDuration }}" onHide="clearToast" />
87
87
  </View>