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