@monimesl/dot-cli 0.1.0-alpha.2 → 0.1.0-alpha.21
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.
- package/README.md +91 -17
- package/dist/cli.js +832 -156
- package/dist/cli.js.map +1 -1
- package/dist/server-B9-F93mS.js +11295 -0
- package/dist/server-B9-F93mS.js.map +1 -0
- package/dist/templates/showcase/.env +3 -0
- package/dist/templates/showcase/dot.ts +52 -0
- package/dist/templates/showcase/pages/basics/bars/controller.ts +36 -0
- package/dist/templates/showcase/pages/basics/bars/state.ts +6 -0
- package/dist/templates/showcase/pages/basics/bars/view.dml +28 -0
- package/dist/templates/showcase/pages/basics/bindings/controller.ts +15 -0
- package/dist/templates/showcase/pages/basics/bindings/state.ts +14 -0
- package/dist/templates/showcase/pages/basics/bindings/view.dml +41 -0
- package/dist/templates/showcase/pages/basics/control-flow/controller.ts +18 -0
- package/dist/templates/showcase/pages/basics/control-flow/state.ts +16 -0
- package/dist/templates/showcase/pages/basics/control-flow/view.dml +55 -0
- package/dist/templates/showcase/pages/basics/events/controller.ts +43 -0
- package/dist/templates/showcase/pages/basics/events/state.ts +11 -0
- package/dist/templates/showcase/pages/basics/events/view.dml +51 -0
- package/dist/templates/showcase/pages/basics/hooks/controller.ts +43 -0
- package/dist/templates/showcase/pages/basics/hooks/state.ts +10 -0
- package/dist/templates/showcase/pages/basics/hooks/view.dml +37 -0
- package/dist/templates/showcase/pages/basics/navigation/controller.ts +41 -0
- package/dist/templates/showcase/pages/basics/navigation/state.ts +11 -0
- package/dist/templates/showcase/pages/basics/navigation/view.dml +48 -0
- package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +12 -0
- package/dist/templates/showcase/pages/basics/pick-genre/state.ts +3 -0
- package/dist/templates/showcase/pages/basics/pick-genre/view.dml +9 -0
- package/dist/templates/showcase/pages/basics/state/controller.ts +23 -0
- package/dist/templates/showcase/pages/basics/state/state.ts +10 -0
- package/dist/templates/showcase/pages/basics/state/view.dml +46 -0
- package/dist/templates/showcase/pages/components/carousel/controller.ts +41 -0
- package/dist/templates/showcase/pages/components/carousel/state.ts +77 -0
- package/dist/templates/showcase/pages/components/carousel/view.dml +42 -0
- package/dist/templates/showcase/pages/components/feedback/controller.ts +32 -0
- package/dist/templates/showcase/pages/components/feedback/state.ts +41 -0
- package/dist/templates/showcase/pages/components/feedback/view.dml +116 -0
- package/dist/templates/showcase/pages/components/forms/controller.ts +72 -0
- package/dist/templates/showcase/pages/components/forms/state.ts +37 -0
- package/dist/templates/showcase/pages/components/forms/view.dml +97 -0
- package/dist/templates/showcase/pages/components/icons/controller.ts +15 -0
- package/dist/templates/showcase/pages/components/icons/state.ts +75 -0
- package/dist/templates/showcase/pages/components/icons/view.dml +36 -0
- package/dist/templates/showcase/pages/components/images/controller.ts +44 -0
- package/dist/templates/showcase/pages/components/images/state.ts +10 -0
- package/dist/templates/showcase/pages/components/images/view.dml +83 -0
- package/dist/templates/showcase/pages/components/layout/controller.ts +61 -0
- package/dist/templates/showcase/pages/components/layout/state.ts +34 -0
- package/dist/templates/showcase/pages/components/layout/view.dml +143 -0
- package/dist/templates/showcase/pages/components/lists/controller.ts +18 -0
- package/dist/templates/showcase/pages/components/lists/state.ts +130 -0
- package/dist/templates/showcase/pages/components/lists/view.dml +88 -0
- package/dist/templates/showcase/pages/components/money/controller.ts +45 -0
- package/dist/templates/showcase/pages/components/money/state.ts +13 -0
- package/dist/templates/showcase/pages/components/money/view.dml +54 -0
- package/dist/templates/showcase/pages/components/reading/controller.ts +6 -0
- package/dist/templates/showcase/pages/components/reading/state.ts +23 -0
- package/dist/templates/showcase/pages/components/reading/view.dml +42 -0
- package/dist/templates/showcase/pages/components/sheets/controller.ts +36 -0
- package/dist/templates/showcase/pages/components/sheets/state.ts +26 -0
- package/dist/templates/showcase/pages/components/sheets/view.dml +38 -0
- package/dist/templates/showcase/pages/components/styles/controller.ts +6 -0
- package/dist/templates/showcase/pages/components/styles/state.ts +3 -0
- package/dist/templates/showcase/pages/components/styles/view.dml +101 -0
- package/dist/templates/showcase/pages/components/tabs/controller.ts +41 -0
- package/dist/templates/showcase/pages/components/tabs/state.ts +127 -0
- package/dist/templates/showcase/pages/components/tabs/view.dml +57 -0
- package/dist/templates/showcase/pages/components/text/controller.ts +10 -0
- package/dist/templates/showcase/pages/components/text/state.ts +3 -0
- package/dist/templates/showcase/pages/components/text/view.dml +95 -0
- package/dist/templates/showcase/pages/dash/cache/controller.ts +50 -0
- package/dist/templates/showcase/pages/dash/cache/state.ts +17 -0
- package/dist/templates/showcase/pages/dash/cache/view.dml +48 -0
- package/dist/templates/showcase/pages/dash/calls/controller.ts +50 -0
- package/dist/templates/showcase/pages/dash/calls/state.ts +6 -0
- package/dist/templates/showcase/pages/dash/calls/view.dml +52 -0
- package/dist/templates/showcase/pages/dash/contacts/controller.ts +30 -0
- package/dist/templates/showcase/pages/dash/contacts/state.ts +6 -0
- package/dist/templates/showcase/pages/dash/contacts/view.dml +33 -0
- package/dist/templates/showcase/pages/dash/files/controller.ts +91 -0
- package/dist/templates/showcase/pages/dash/files/state.ts +24 -0
- package/dist/templates/showcase/pages/dash/files/view.dml +79 -0
- package/dist/templates/showcase/pages/dash/media/controller.ts +36 -0
- package/dist/templates/showcase/pages/dash/media/state.ts +8 -0
- package/dist/templates/showcase/pages/dash/media/view.dml +34 -0
- package/dist/templates/showcase/pages/dash/network/controller.ts +215 -0
- package/dist/templates/showcase/pages/dash/network/state.ts +35 -0
- package/dist/templates/showcase/pages/dash/network/view.dml +108 -0
- package/dist/templates/showcase/pages/dash/secrets/controller.ts +44 -0
- package/dist/templates/showcase/pages/dash/secrets/state.ts +5 -0
- package/dist/templates/showcase/pages/dash/secrets/view.dml +26 -0
- package/dist/templates/showcase/pages/dash/system/controller.ts +89 -0
- package/dist/templates/showcase/pages/dash/system/state.ts +13 -0
- package/dist/templates/showcase/pages/dash/system/view.dml +97 -0
- package/dist/templates/showcase/pages/dash/ui/controller.ts +70 -0
- package/dist/templates/showcase/pages/dash/ui/state.ts +8 -0
- package/dist/templates/showcase/pages/dash/ui/view.dml +31 -0
- package/dist/templates/showcase/pages/index/controller.ts +10 -0
- package/dist/templates/showcase/pages/index/state.ts +212 -0
- package/dist/templates/showcase/pages/index/view.dml +23 -0
- package/dist/templates/showcase/statics/logo.png +0 -0
- package/dist/templates/starter/.env +3 -0
- package/dist/templates/starter/dot.ts +3 -0
- package/dist/templates/starter/pages/index/controller.ts +9 -0
- package/dist/templates/starter/pages/index/state.ts +4 -0
- package/dist/templates/starter/pages/index/view.dml +16 -0
- package/package.json +12 -7
- package/dist/template/.env +0 -3
- package/dist/template/dot.ts +0 -6
- package/dist/template/pages/help/tips/controller.ts +0 -30
- package/dist/template/pages/help/tips/state.ts +0 -11
- package/dist/template/pages/help/tips/view.dml +0 -15
- package/dist/template/pages/index/controller.ts +0 -67
- package/dist/template/pages/index/state.ts +0 -17
- package/dist/template/pages/index/view.dml +0 -66
- /package/dist/{template → templates/starter}/statics/logo.png +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export type Kind = 'album' | 'song';
|
|
2
|
+
export type Genre = 'Kriofusion' | 'Afropop' | 'Hip hop' | 'R&B' | 'Palm-wine' | 'Gospel' | 'Bubu';
|
|
3
|
+
|
|
4
|
+
export interface Release {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
artist: string;
|
|
8
|
+
year: number;
|
|
9
|
+
kind: Kind;
|
|
10
|
+
genre: Genre;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Show {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
place: string;
|
|
17
|
+
date: string;
|
|
18
|
+
city: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function release(
|
|
22
|
+
id: string,
|
|
23
|
+
title: string,
|
|
24
|
+
artist: string,
|
|
25
|
+
year: number,
|
|
26
|
+
kind: Kind,
|
|
27
|
+
genre: Genre,
|
|
28
|
+
): Release {
|
|
29
|
+
return { id, title, artist, year, kind, genre };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Newest first.
|
|
33
|
+
const releases: Release[] = [
|
|
34
|
+
release('audacity', 'AUDACITY', 'Drizilik', 2026, 'album', 'Kriofusion'),
|
|
35
|
+
release('money-or-honey', 'Money or Honey', 'Boii', 2025, 'album', 'R&B'),
|
|
36
|
+
release('control', 'Control', 'ApreeL', 2025, 'song', 'Afropop'),
|
|
37
|
+
release('wait-and-see', 'Wait and See', 'Drizilik', 2025, 'album', 'Kriofusion'),
|
|
38
|
+
release('blunt', 'Blunt', 'Donii Major', 2023, 'song', 'R&B'),
|
|
39
|
+
release('ashobi', 'Ashobi', 'Drizilik', 2022, 'album', 'Kriofusion'),
|
|
40
|
+
release('nack', 'Nack', 'The Therapist', 2022, 'song', 'Afropop'),
|
|
41
|
+
release('hannah', 'Hannah', 'Donii Major', 2021, 'song', 'R&B'),
|
|
42
|
+
release('shukubly', 'Shukubly', 'Drizilik', 2018, 'album', 'Kriofusion'),
|
|
43
|
+
release('en-yay-sah', 'En Yay Sah', 'Janka Nabay and the Bubu Gang', 2012, 'album', 'Bubu'),
|
|
44
|
+
release('red-album', 'Red Album', 'LAJ', 2013, 'album', 'Hip hop'),
|
|
45
|
+
release(
|
|
46
|
+
'ghetto-struggle',
|
|
47
|
+
'Ghetto Struggle',
|
|
48
|
+
'Kao Denero, with Busy Signal',
|
|
49
|
+
2013,
|
|
50
|
+
'song',
|
|
51
|
+
'Hip hop',
|
|
52
|
+
),
|
|
53
|
+
release('borbor-bele', 'Borbor Bele', 'Emmerson', 2004, 'album', 'Afropop'),
|
|
54
|
+
release('corruption', 'Corruption', 'Daddy Saj', 2003, 'album', 'Hip hop'),
|
|
55
|
+
release('cry-africa', 'Cry Africa', 'Vicky Fornah', 1999, 'song', 'Gospel'),
|
|
56
|
+
release('kormot-bi-en-me', 'Kormot Bi En Me', 'Steady Bongo', 1996, 'album', 'Afropop'),
|
|
57
|
+
release('dead-men', "Dead Men Don't Smoke Marijuana", 'S. E. Rogie', 1994, 'album', 'Palm-wine'),
|
|
58
|
+
release('let-me-love-you', 'Let Me Love You', 'Bunny Mack', 1981, 'song', 'Afropop'),
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
export default {
|
|
62
|
+
releases,
|
|
63
|
+
// The releases the tab and the chosen genre leave.
|
|
64
|
+
shown: releases,
|
|
65
|
+
tab: 'all',
|
|
66
|
+
albums: releases.filter((item) => item.kind === 'album').length,
|
|
67
|
+
songs: releases.filter((item) => item.kind === 'song').length,
|
|
68
|
+
genres: ['All', 'Kriofusion', 'Afropop', 'Hip hop', 'R&B', 'Palm-wine', 'Gospel', 'Bubu'],
|
|
69
|
+
genre: 'All',
|
|
70
|
+
query: '',
|
|
71
|
+
liked: false,
|
|
72
|
+
// Shows at home in Sierra Leone, and abroad in the Mano River region.
|
|
73
|
+
where: 'home',
|
|
74
|
+
home: [
|
|
75
|
+
{
|
|
76
|
+
id: 'sledu-2026',
|
|
77
|
+
title: 'SLeDU National Music Awards',
|
|
78
|
+
place: 'Bintumani Conference Centre',
|
|
79
|
+
date: '27 March 2026',
|
|
80
|
+
city: 'Freetown',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'special-guests-4',
|
|
84
|
+
title: 'Drizilik & Special Guests',
|
|
85
|
+
place: 'Fourah Bay College Amphitheatre',
|
|
86
|
+
date: '28 November 2025',
|
|
87
|
+
city: 'Freetown',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'christmas-jam',
|
|
91
|
+
title: 'Christmas Jam',
|
|
92
|
+
place: 'Coronation Field',
|
|
93
|
+
date: '26 December 2025',
|
|
94
|
+
city: 'Bo',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'gospel-night',
|
|
98
|
+
title: 'Gospel Night',
|
|
99
|
+
place: 'Kenema City Hall',
|
|
100
|
+
date: '15 November 2025',
|
|
101
|
+
city: 'Kenema',
|
|
102
|
+
},
|
|
103
|
+
] as Show[],
|
|
104
|
+
abroad: [
|
|
105
|
+
{
|
|
106
|
+
id: 'monrovia-live',
|
|
107
|
+
title: 'Salone Sounds Live',
|
|
108
|
+
place: 'EJS Ministerial Complex',
|
|
109
|
+
date: '20 December 2025',
|
|
110
|
+
city: 'Monrovia',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'mano-river-festival',
|
|
114
|
+
title: 'Mano River Music Festival',
|
|
115
|
+
place: 'Palais du Peuple',
|
|
116
|
+
date: '8 November 2025',
|
|
117
|
+
city: 'Conakry',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'abidjan-night',
|
|
121
|
+
title: 'Salone Night',
|
|
122
|
+
place: 'Palais de la Culture',
|
|
123
|
+
date: '13 September 2025',
|
|
124
|
+
city: 'Abidjan',
|
|
125
|
+
},
|
|
126
|
+
] as Show[],
|
|
127
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<View>
|
|
2
|
+
<Text variant="title">Tabs and chips</Text>
|
|
3
|
+
<Text>Tabs change what the page shows. Chips and a search narrow it down. Try Songs with Palm-wine: nothing matches.</Text>
|
|
4
|
+
|
|
5
|
+
<View gap="small">
|
|
6
|
+
<Text variant="subtitle">Tabs and Chip</Text>
|
|
7
|
+
<Text variant="caption" tone="muted">The underline tabs, with a badge for each count. A chip is selected when the page says so. An Input with type="search" and clearable makes a search box.</Text>
|
|
8
|
+
<Tabs variant="underline" value="{{ tab }}" onChange="chooseTab">
|
|
9
|
+
<Tab value="all" label="All" />
|
|
10
|
+
<Tab value="album" label="Albums" badge="{{ albums }}" />
|
|
11
|
+
<Tab value="song" label="Songs" badge="{{ songs }}" />
|
|
12
|
+
</Tabs>
|
|
13
|
+
<Input type="search" placeholder="Search by title" clearable value="{{ query }}" onChange="search" />
|
|
14
|
+
<View direction="row" gap="small" wrap>
|
|
15
|
+
<For each="{{ genres }}" item="option" key="*this">
|
|
16
|
+
<Chip label="{{ option }}" selected="{{ option === genre }}" data-genre="{{ option }}" onTap="chooseGenre" />
|
|
17
|
+
</For>
|
|
18
|
+
</View>
|
|
19
|
+
<If condition="{{ shown.length > 0 }}">
|
|
20
|
+
<List>
|
|
21
|
+
<For each="{{ shown }}" item="release" key="id">
|
|
22
|
+
<ListItem icon="{{ release.kind === 'album' ? 'playlist' : 'music' }}" title="{{ release.title }}" subtitle="{{ release.artist }} · {{ release.genre }}">
|
|
23
|
+
<Badge>{{ release.year }}</Badge>
|
|
24
|
+
</ListItem>
|
|
25
|
+
</For>
|
|
26
|
+
</List>
|
|
27
|
+
</If>
|
|
28
|
+
<Else>
|
|
29
|
+
<EmptyState icon="search" title="Nothing here" text="No release matches this tab, genre and search.">
|
|
30
|
+
<Button variant="secondary" onClick="clearFilters">Clear filters</Button>
|
|
31
|
+
</EmptyState>
|
|
32
|
+
</Else>
|
|
33
|
+
</View>
|
|
34
|
+
|
|
35
|
+
<View gap="small">
|
|
36
|
+
<Text variant="subtitle">Chip with an icon</Text>
|
|
37
|
+
<Text variant="caption" tone="muted">A disabled chip can't be tapped, such as a filter with nothing behind it.</Text>
|
|
38
|
+
<View direction="row" gap="small" wrap>
|
|
39
|
+
<Chip icon="heart" label="Liked" selected="{{ liked }}" onTap="toggleLiked" />
|
|
40
|
+
<Chip icon="download" label="Downloaded" disabled />
|
|
41
|
+
</View>
|
|
42
|
+
</View>
|
|
43
|
+
|
|
44
|
+
<View gap="small">
|
|
45
|
+
<Text variant="subtitle">variant="segmented"</Text>
|
|
46
|
+
<Text variant="caption" tone="muted">For a few short choices. Shows at home and in the Mano River region.</Text>
|
|
47
|
+
<Tabs variant="segmented" value="{{ where }}" onChange="chooseWhere">
|
|
48
|
+
<Tab value="home" label="Sierra Leone" />
|
|
49
|
+
<Tab value="abroad" label="Abroad" />
|
|
50
|
+
</Tabs>
|
|
51
|
+
<List>
|
|
52
|
+
<For each="{{ where === 'home' ? home : abroad }}" item="show" key="id">
|
|
53
|
+
<ListItem icon="ticket" title="{{ show.title }}" subtitle="{{ show.place }} · {{ show.date }}" value="{{ show.city }}" />
|
|
54
|
+
</For>
|
|
55
|
+
</List>
|
|
56
|
+
</View>
|
|
57
|
+
</View>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import state from './state';
|
|
2
|
+
|
|
3
|
+
export default definePage({
|
|
4
|
+
initialState: state,
|
|
5
|
+
configs: { navigationBar: { title: 'Text and links' } },
|
|
6
|
+
mutations: {
|
|
7
|
+
like: (state) => ({ likes: state.likes + 1 }),
|
|
8
|
+
unlike: (state) => ({ likes: Math.max(0, state.likes - 1) }),
|
|
9
|
+
},
|
|
10
|
+
});
|
|
@@ -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,50 @@
|
|
|
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
|
+
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_SECONDS,
|
|
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 keeps it for 30 seconds.</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,50 @@
|
|
|
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
|
+
actions: {
|
|
13
|
+
// Makes the call a button names, and shows how it ended.
|
|
14
|
+
async show(_state, event: DataEvent<{ call: string }>) {
|
|
15
|
+
this.setState({ code: '', message: '' });
|
|
16
|
+
try {
|
|
17
|
+
switch (event.data.call) {
|
|
18
|
+
case 'timeout':
|
|
19
|
+
// Gives up after 1 second on a wait of 5.
|
|
20
|
+
await this.dash.system.delay(5000, { timeout: 1000 });
|
|
21
|
+
break;
|
|
22
|
+
case 'cancelled':
|
|
23
|
+
wait = this.dash.system.delay(10_000);
|
|
24
|
+
this.setState({ waiting: true });
|
|
25
|
+
await wait;
|
|
26
|
+
break;
|
|
27
|
+
case 'invalid':
|
|
28
|
+
await this.dash.ui.pickDate({ min: '2026-12-31', max: '2026-01-01' });
|
|
29
|
+
break;
|
|
30
|
+
case 'denied':
|
|
31
|
+
await this.dash.network.http.request({ url: 'https://example.com' });
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
this.setState({ code: 'none', message: 'It worked' });
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (isDashError(error)) {
|
|
37
|
+
this.setState({ code: error.code, message: error.message });
|
|
38
|
+
} else {
|
|
39
|
+
this.setState({ code: 'not a DashError', message: errorMessage(error) });
|
|
40
|
+
}
|
|
41
|
+
} finally {
|
|
42
|
+
wait = null;
|
|
43
|
+
this.setState({ waiting: false });
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
cancelWait() {
|
|
47
|
+
wait?.cancel();
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
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. Each code has its own check too, such as isDashCancelled(error).</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="failed" value="Anything else went wrong" />
|
|
30
|
+
</View>
|
|
31
|
+
<View direction="row" wrap gap="small">
|
|
32
|
+
<Button variant="secondary" data-call="invalid" onClick="show">Pick a date between bad limits</Button>
|
|
33
|
+
<Button variant="secondary" data-call="denied" onClick="show">Reach an unlisted host</Button>
|
|
34
|
+
</View>
|
|
35
|
+
</View>
|
|
36
|
+
|
|
37
|
+
<View gap="small">
|
|
38
|
+
<Text variant="subtitle">The last call</Text>
|
|
39
|
+
<View variant="card" gap="none">
|
|
40
|
+
<If condition="{{ code }}">
|
|
41
|
+
<DetailRow label="error.code" value="{{ code }}" weight="bold" />
|
|
42
|
+
<DetailRow label="error.message" value="{{ message }}" />
|
|
43
|
+
</If>
|
|
44
|
+
<ElseIf condition="{{ waiting }}">
|
|
45
|
+
<Text tone="muted">Waiting…</Text>
|
|
46
|
+
</ElseIf>
|
|
47
|
+
<Else>
|
|
48
|
+
<Text tone="muted">Use a button above</Text>
|
|
49
|
+
</Else>
|
|
50
|
+
</View>
|
|
51
|
+
</View>
|
|
52
|
+
</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,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>
|