AT Protocol Jetstream Module for Open Web Desktop
1import { useApplicationManager } from "@owdproject/core/runtime/composables/useApplicationManager"
2import { useDesktopStore } from '@owdproject/core/runtime/stores/storeDesktop'
3import { useRuntimeConfig } from 'nuxt/app'
4
5function getJetstreamUrl(host: string, actorDid: string) {
6 const url = new URL('subscribe', `wss://${host}`)
7 url.searchParams.append('wantedDids', actorDid)
8 url.searchParams.append(
9 'wantedCollections',
10 'org.owdproject.application.desktop'
11 )
12 url.searchParams.append(
13 'wantedCollections',
14 'org.owdproject.application.windows'
15 )
16 url.searchParams.append(
17 'wantedCollections',
18 'org.owdproject.application.meta'
19 )
20 return url.toString()
21}
22
23/**
24 * Load actor desktop
25 *
26 * @param actorDid
27 */
28export async function startActorDesktopStreamw(actorDid: string) {
29 const applicationManager = useApplicationManager()
30 const runtimeConfig = useRuntimeConfig()
31
32 let ws: WebSocket | null = null
33
34 const handleMessage = (event: MessageEvent) => {
35 try {
36 const data = JSON.parse(event.data)
37
38 let applicationController
39 let atprotoApplicationId
40
41 switch (data.commit.collection) {
42 case 'org.owdproject.desktop':
43 useDesktopStore().$patch(data.commit.record.state)
44 break
45 case 'org.owdproject.application.windows':
46 atprotoApplicationId = data.commit.rkey.split('/').pop()
47
48 // get the application controller by its id
49 applicationController = applicationManager.getAppById(atprotoApplicationId)
50
51 // if the application doesn't exist, exit early
52 if (!applicationController) {
53 return
54 }
55
56 const isApplicationNowRunning = Object.keys(data.commit.record.windows).length > 0
57
58 applicationController.setRunning(isApplicationNowRunning)
59
60 let windowController
61
62 // iterate through all windows in the received remote state
63 for (const windowId in data.commit.record.windows) {
64 // try to find an existing local window controller
65 windowController = applicationController.getWindowById(windowId)
66
67 if (!windowController) {
68 // if the window doesn't exist locally, create it
69 return applicationController.openWindow(
70 data.commit.record.windows[windowId].model,
71 data.commit.record.windows[windowId],
72 {
73 isRestoring: true
74 }
75 )
76 } else {
77 // if the window exists, update its state with the remote one
78 windowController.setState(
79 data.commit.record.windows[windowId].state
80 )
81 }
82 }
83
84 // second loop: close any local windows that are not in the remote state anymore
85 for (const windowId of applicationController.windows.keys()) {
86 if (!data.commit.record.windows[windowId]) {
87 // the window is no longer present remotely, so close it locally
88 applicationController.closeWindow(windowId)
89 }
90 }
91
92 break
93 case 'org.owdproject.application.meta':
94 atprotoApplicationId = data.commit.rkey.split('/').pop()
95
96 applicationController.storeMeta.$patch({
97 ...data.commit.record
98 })
99 break
100 }
101 } catch (e) {
102 console.warn('Invalid jetstream message', e)
103 }
104 }
105
106 ws = new WebSocket(
107 getJetstreamUrl(runtimeConfig.public.desktop.atprotoJetstream.host, actorDid)
108 )
109 ws.addEventListener('message', handleMessage)
110}