create-wirejs-app 2.0.94 → 2.0.96-async-api
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/package.json
CHANGED
|
@@ -1,16 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AuthenticationApi,
|
|
3
|
+
BackgroundJob,
|
|
4
|
+
RealtimeService,
|
|
5
|
+
withContext,
|
|
6
|
+
} from "wirejs-resources";
|
|
2
7
|
|
|
3
8
|
const realtimeService = new RealtimeService<{
|
|
4
9
|
username: string;
|
|
5
10
|
body: string;
|
|
6
11
|
}>('app', 'realtime');
|
|
7
12
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const counter = new BackgroundJob('app', 'countdowns', {
|
|
14
|
+
handler: async (room: string, seconds: number) => {
|
|
15
|
+
return new Promise<void>((resolve) => {
|
|
16
|
+
let remaining = seconds;
|
|
17
|
+
const interval = setInterval(() => {
|
|
18
|
+
if (remaining <= 0) {
|
|
19
|
+
clearInterval(interval);
|
|
20
|
+
resolve();
|
|
21
|
+
} else {
|
|
22
|
+
realtimeService.publish(sanitizedRoomName(room), [{
|
|
23
|
+
username: 'Countdown',
|
|
24
|
+
body: `Time remaining: ${remaining--} seconds`
|
|
25
|
+
}]);
|
|
26
|
+
}
|
|
27
|
+
}, 1000);
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
});
|
|
14
31
|
|
|
15
32
|
export const Chat = (auth: AuthenticationApi) => withContext(context => ({
|
|
16
33
|
async publish(room: string, message: string) {
|
|
@@ -23,5 +40,19 @@ export const Chat = (auth: AuthenticationApi) => withContext(context => ({
|
|
|
23
40
|
async getRoom(room: string) {
|
|
24
41
|
await auth.requireCurrentUser(context);
|
|
25
42
|
return realtimeService.getStream(sanitizedRoomName(room));
|
|
43
|
+
},
|
|
44
|
+
async startCountdown(room: string, seconds: number) {
|
|
45
|
+
await auth.requireCurrentUser(context);
|
|
46
|
+
if (seconds < 5 || seconds > 600) {
|
|
47
|
+
throw new Error('Countdown must be between 5 and 60 seconds.');
|
|
48
|
+
}
|
|
49
|
+
await counter.start(sanitizedRoomName(room), seconds);
|
|
26
50
|
}
|
|
27
|
-
}));
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
function sanitizedRoomName(room: string | null): string {
|
|
54
|
+
if (room === null || room === '') {
|
|
55
|
+
return 'default';
|
|
56
|
+
}
|
|
57
|
+
return room.replace(/[^-_a-zA-Z0-9]/g, '-').slice(0, 50);
|
|
58
|
+
}
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
"dompurify": "^3.2.3",
|
|
12
12
|
"marked": "^15.0.6",
|
|
13
13
|
"wirejs-dom": "^1.0.41",
|
|
14
|
-
"wirejs-resources": "^0.1.
|
|
15
|
-
"wirejs-components": "^0.1.
|
|
14
|
+
"wirejs-resources": "^0.1.91-async-api",
|
|
15
|
+
"wirejs-components": "^0.1.34-async-api"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"wirejs-scripts": "^3.0.
|
|
18
|
+
"wirejs-scripts": "^3.0.89-async-api",
|
|
19
19
|
"typescript": "^5.7.3"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
@@ -18,7 +18,7 @@ async function Chat() {
|
|
|
18
18
|
marked.parse(`**${message.username}:** ${message.body}`
|
|
19
19
|
) as string))}`)}
|
|
20
20
|
|
|
21
|
-
<!--
|
|
21
|
+
<!-- New message form -->
|
|
22
22
|
<form onsubmit=${(event: Event) => {
|
|
23
23
|
event.preventDefault();
|
|
24
24
|
chat.publish(null, self.data.room, self.data.message);
|
|
@@ -31,7 +31,7 @@ async function Chat() {
|
|
|
31
31
|
<!-- Connection status -->
|
|
32
32
|
<span style='color: var(--color-muted)'>${text('status', 'Connecting ...')}</span>
|
|
33
33
|
|
|
34
|
-
<!-- Room selection -->
|
|
34
|
+
<!-- Room selection form -->
|
|
35
35
|
<form ${id('roomChangeForm')} onsubmit=${(event: Event) => {
|
|
36
36
|
event.preventDefault();
|
|
37
37
|
self.data.messages = [];
|
|
@@ -49,8 +49,31 @@ async function Chat() {
|
|
|
49
49
|
}} />
|
|
50
50
|
</form>
|
|
51
51
|
|
|
52
|
+
<!-- Countdown form -->
|
|
53
|
+
<form ${id('countdownForm')} onsubmit=${(event: Event) => {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
if (self.data.countdownDisabled) {
|
|
56
|
+
// discourage spamming the countdown
|
|
57
|
+
alert('Countdown is already running. One at a time please!');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
self.data.countdownDisabled = true;
|
|
61
|
+
chat.startCountdown(null, self.data.room, self.data.countdownSeconds);
|
|
62
|
+
self.data.countdownSeconds = 10;
|
|
63
|
+
setTimeout(() => {
|
|
64
|
+
self.data.countdownDisabled = false;
|
|
65
|
+
}, self.data.countdownSeconds * 1000);
|
|
66
|
+
}}>
|
|
67
|
+
<input type='hidden' value=${attribute('countdownDisabled', false as boolean)} />
|
|
68
|
+
<input type='number' style='width: 10rem;'
|
|
69
|
+
value=${attribute('countdownSeconds', 10 as number)}
|
|
70
|
+
min='5' max='60' />
|
|
71
|
+
<input type='submit' style='width: 10rem;' value='Start Counting' />
|
|
72
|
+
</form>
|
|
73
|
+
|
|
52
74
|
<!-- Description -->
|
|
53
75
|
<p>A simple example of realtime messaging. Messages are 100% ephemeral. If you reload the page, messages are lost. If you're not connected when a message it sent, you won't receive it.</p>
|
|
76
|
+
<p>The countdown feature is a simple background job that sends a message every second until the countdown reaches zero.</p>
|
|
54
77
|
</div>`.extend(() => ({
|
|
55
78
|
disconnect() {
|
|
56
79
|
// no implementation until connected
|