tangled
alpha
login
or
join now
accidental.cc
/
skypod
3
fork
atom
podcast manager
3
fork
atom
overview
issues
pulls
pipelines
fixed custom event bubbling
Jonathan Raphaelson
5 months ago
ad186614
58258df2
+32
-19
5 changed files
expand all
collapse all
unified
split
src
client
page-app.tsx
realm
service-connection.ts
skypod
context.tsx
feed-fetch.worker.ts
cmd
server.ts
+5
-1
src/client/page-app.tsx
···
17
17
const connectionFallback = (p: RealmConnectionFallbackProps) =>
18
18
p.error ? <h1>Connection Error! {p.error.message}</h1> : <h1>Connection Loading</h1>
19
19
20
20
+
const wshost = window.location.host
21
21
+
const wsproto = window.location.protocol === 'https' ? 'wss' : 'ws'
22
22
+
const wsurl = `${wsproto}://${wshost}/stream`
23
23
+
20
24
return (
21
25
<DatabaseProvider>
22
26
<RealmIdentityProvider fallback={identityFallback}>
23
23
-
<RealmConnectionProvider fallback={connectionFallback} url="ws://localhost:3001/stream">
27
27
+
<RealmConnectionProvider fallback={connectionFallback} url={wsurl}>
24
28
<SkypodProvider>
25
29
<RealmConnectionManager />
26
30
<PeerList />
+4
-1
src/client/realm/service-connection.ts
···
92
92
}
93
93
94
94
send<T extends unknown>(identid: IdentID, data: T) {
95
95
+
console.debug('sending:', identid, data)
95
96
this.sendRaw(identid, JSON.stringify(data))
96
97
}
97
98
···
103
104
}
104
105
105
106
broadcast(data: unknown, self = false) {
107
107
+
console.debug('broadcasting:', self, data)
106
108
this.broadcastRaw(JSON.stringify(data), self)
107
109
}
108
110
···
128
130
}
129
131
130
132
#dispatchCustomEvent(type: string, detail?: object) {
131
131
-
this.dispatchEvent(new CustomEvent(type, {detail}))
133
133
+
this.dispatchEvent(new CustomEvent(type, {bubbles: true, detail}))
132
134
}
133
135
134
136
// typed helpers
···
224
226
for (const peerid of resp.dat.peers) {
225
227
if (peerid === this.#identity.identid) continue
226
228
229
229
+
console.debug('connecting...:', peerid)
227
230
this.#connectPeer(peerid, true)
228
231
}
229
232
+20
-16
src/client/skypod/context.tsx
···
1
1
-
import {useSignalEffect} from '@preact/signals'
2
1
import {createContext} from 'preact'
3
2
import {useContext, useEffect, useRef} from 'preact/hooks'
4
3
import {z} from 'zod/v4'
···
81
80
},
82
81
])
83
82
84
84
-
const context: SkypodContext = {
83
83
+
const context = useRef<SkypodContext>({
85
84
dispatch: async <K extends keyof ActionMap>(action: ActionMap[K]) => {
86
85
const actions = [action] as Action[]
86
86
+
87
87
+
// broadcast if not a local action
88
88
+
if (!action.opt?.local) {
89
89
+
realm.value?.broadcast(action, false)
90
90
+
// TODO: queue these for rebroadcast if not connected
91
91
+
}
87
92
88
93
for (const action of actions) {
89
94
for (const mware of middleware.current) {
···
98
103
actions.push(...result)
99
104
}
100
105
}
101
101
-
}
102
102
-
103
103
-
// no-op if not connected
104
104
-
if (!action.opt?.local) {
105
105
-
realm.value?.broadcast(action, false)
106
106
}
107
107
}
108
108
},
···
151
151
middleware.current = [...prefix, ...suffix]
152
152
}
153
153
},
154
154
-
}
154
154
+
})
155
155
156
156
// watch the connection
157
157
// while we're connected, watch peers for action messages
158
158
-
useSignalEffect(() => {
158
158
+
useEffect(() => {
159
159
const connection = realm.value
160
160
if (!connection?.connected) return
161
161
···
165
165
const parsed = schema.safeParse(event.detail.data)
166
166
if (parsed.success) {
167
167
console.log('handling forwarded event:', parsed)
168
168
-
context.dispatch({...parsed.data, opt: {local: true}}).catch((ex: unknown) => {
168
168
+
context.current.dispatch({...parsed.data, opt: {local: true}}).catch((ex: unknown) => {
169
169
console.error('couldnt dispatch realm action!', ex)
170
170
})
171
171
}
···
175
175
return () => {
176
176
connection.removeEventListener('peerdata', handler as EventListener)
177
177
}
178
178
-
})
178
178
+
}, [context, realm.value])
179
179
180
180
const patchSchema = z.union([
181
181
z.object({
···
199
199
console.log('message from fetch worker', parsed)
200
200
201
201
switch (parsed.data?.msg) {
202
202
-
case 'patch':
203
203
-
await context.dispatch(
204
204
-
context.action('feed:patch', {url: parsed.data.key, payload: parsed.data.changes}),
205
205
-
)
202
202
+
case 'patch': {
203
203
+
const action = context.current.action('feed:patch', {
204
204
+
url: parsed.data.key,
205
205
+
payload: parsed.data.changes,
206
206
+
})
207
207
+
console.log('sending action:', action)
208
208
+
await context.current.dispatch(action)
206
209
break
210
210
+
}
207
211
208
212
case 'error':
209
213
default:
···
224
228
})
225
229
226
230
console.log('rendering the skypod context')
227
227
-
return <SkypodContext.Provider value={context}>{props.children}</SkypodContext.Provider>
231
231
+
return <SkypodContext.Provider value={context.current}>{props.children}</SkypodContext.Provider>
228
232
}
229
233
230
234
export function useSkypod() {
+2
src/client/skypod/feed-fetch.worker.ts
···
74
74
key: feed.url,
75
75
changes: {
76
76
...parsed,
77
77
+
lock: null,
77
78
lastRefresh: {
78
79
hp: 100,
79
80
at: lock,
···
91
92
msg: 'patch',
92
93
key: feed.url,
93
94
changes: {
95
95
+
lock: null,
94
96
lastRefresh: {
95
97
hp: feed.lastRefresh.hp - 10,
96
98
at: lock,
+1
-1
src/cmd/server.ts
···
11
11
const args = parseArgs({
12
12
args: process.argv.slice(2),
13
13
options: {
14
14
-
port: {type: 'string', default: '3001'},
14
14
+
port: {type: 'string', default: '4001'},
15
15
host: {type: 'string', default: '127.0.0.1'},
16
16
},
17
17
})