perf: multiple peerRoom implementation
This commit is contained in:
parent
ec62b1155e
commit
e373993899
15 changed files with 628 additions and 276 deletions
|
@ -56,8 +56,11 @@
|
|||
"@radix-ui/react-scroll-area": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.1.0",
|
||||
"@resreq/event-hub": "^1.6.0",
|
||||
"@resreq/timer": "^1.1.5",
|
||||
"@rtco/client": "^0.2.17",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@webext-core/proxy-service": "^1.2.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
|
|
219
pnpm-lock.yaml
219
pnpm-lock.yaml
|
@ -44,12 +44,21 @@ importers:
|
|||
'@radix-ui/react-switch':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@resreq/event-hub':
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0
|
||||
'@resreq/timer':
|
||||
specifier: ^1.1.5
|
||||
version: 1.1.5
|
||||
'@rtco/client':
|
||||
specifier: ^0.2.17
|
||||
version: 0.2.17
|
||||
'@tailwindcss/typography':
|
||||
specifier: ^0.5.15
|
||||
version: 0.5.15(tailwindcss@3.4.12)
|
||||
'@webext-core/proxy-service':
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0(@webext-core/messaging@1.4.0)(webextension-polyfill@0.12.0)
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
|
@ -115,7 +124,7 @@ importers:
|
|||
version: 2.5.2
|
||||
trystero:
|
||||
specifier: ^0.20.0
|
||||
version: 0.20.0(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/proto@0.0.7)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20)
|
||||
version: 0.20.0(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/proto@0.0.7)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20)(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
type-fest:
|
||||
specifier: ^4.26.1
|
||||
version: 4.26.1
|
||||
|
@ -227,7 +236,7 @@ importers:
|
|||
version: 6.0.1
|
||||
wxt:
|
||||
specifier: ^0.19.9
|
||||
version: 0.19.9(@types/node@22.5.5)(rollup@4.21.3)
|
||||
version: 0.19.9(@types/node@22.5.5)(bufferutil@4.0.8)(rollup@4.21.3)(utf-8-validate@6.0.4)
|
||||
|
||||
packages:
|
||||
|
||||
|
@ -1741,6 +1750,12 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rtco/client@0.2.17':
|
||||
resolution: {integrity: sha512-nV/KJGBh/j0fK069uADXNr30JBbWe7CZ0xe0cEx1JjuBQIkH10Ny5mQm4WZd/vID+EF7l+tqkCG6QUyCAW5PFg==}
|
||||
|
||||
'@rtco/peer@0.2.17':
|
||||
resolution: {integrity: sha512-jxKQzAIMiofkJ5UHIbeq2JUl+fBOCnWRxgxemzuI7TKw96pbkfaMowr3fj+ElXnKPWwCi5WRX3hwitqjfNkwFQ==}
|
||||
|
||||
'@sec-ant/readable-stream@0.4.1':
|
||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||
|
||||
|
@ -1756,6 +1771,9 @@ packages:
|
|||
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@socket.io/component-emitter@3.1.2':
|
||||
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
|
||||
|
||||
'@supabase/auth-js@2.65.0':
|
||||
resolution: {integrity: sha512-+wboHfZufAE2Y612OsKeVP4rVOeGZzzMLD/Ac3HrTQkkY4qXNjI6Af9gtmxwccE5nFvTiF114FEbIQ1hRq5uUw==}
|
||||
|
||||
|
@ -2105,6 +2123,15 @@ packages:
|
|||
'@webext-core/match-patterns@1.0.3':
|
||||
resolution: {integrity: sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A==}
|
||||
|
||||
'@webext-core/messaging@1.4.0':
|
||||
resolution: {integrity: sha512-gzXQ13HfKR3Yrn9TnrvTC/5seA7uPFvaqxqNFBsFOOdSZa5LyXt58Rhym8BYXarkWUGp+fh8f6AYM3RYuNbS+A==}
|
||||
|
||||
'@webext-core/proxy-service@1.2.0':
|
||||
resolution: {integrity: sha512-MCUadVakeb7L47AvdtlbJfBUDjFdejr5t4E2WrwZagnev3a5I/xh2wHCkE+G0ihO/VUt/m0R1MPX+y4YVFRyPA==}
|
||||
peerDependencies:
|
||||
'@webext-core/messaging': '>=1.3.1'
|
||||
webextension-polyfill: ^0.10.0
|
||||
|
||||
'@xobotyi/scrollbar-width@1.9.5':
|
||||
resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==}
|
||||
|
||||
|
@ -2311,6 +2338,10 @@ packages:
|
|||
buffer@6.0.3:
|
||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||
|
||||
bufferutil@4.0.8:
|
||||
resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==}
|
||||
engines: {node: '>=6.14.2'}
|
||||
|
||||
bundle-name@3.0.0:
|
||||
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -2825,6 +2856,13 @@ packages:
|
|||
end-of-stream@1.4.4:
|
||||
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||
|
||||
engine.io-client@6.6.1:
|
||||
resolution: {integrity: sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==}
|
||||
|
||||
engine.io-parser@5.2.3:
|
||||
resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
@ -3271,6 +3309,10 @@ packages:
|
|||
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
get-value@3.0.1:
|
||||
resolution: {integrity: sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==}
|
||||
engines: {node: '>=6.0'}
|
||||
|
||||
giget@1.2.3:
|
||||
resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==}
|
||||
hasBin: true
|
||||
|
@ -4415,6 +4457,10 @@ packages:
|
|||
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
||||
engines: {node: '>= 6.13.0'}
|
||||
|
||||
node-gyp-build@4.8.2:
|
||||
resolution: {integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==}
|
||||
hasBin: true
|
||||
|
||||
node-notifier@10.0.1:
|
||||
resolution: {integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==}
|
||||
|
||||
|
@ -5128,6 +5174,10 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
serialize-error@11.0.3:
|
||||
resolution: {integrity: sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
serialize-error@9.1.1:
|
||||
resolution: {integrity: sha512-6uZQLGyUkNA4N+Zii9fYukmNu9PEA1F5rqcwXzN/3LtBjwl2dFBbVZ1Zyn08/CGkB4H440PIemdOQBt1Wvjbrg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
@ -5211,6 +5261,14 @@ packages:
|
|||
resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
socket.io-client@4.8.0:
|
||||
resolution: {integrity: sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
socket.io-parser@4.2.4:
|
||||
resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
sonner@1.5.0:
|
||||
resolution: {integrity: sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==}
|
||||
peerDependencies:
|
||||
|
@ -5756,6 +5814,10 @@ packages:
|
|||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
utf-8-validate@6.0.4:
|
||||
resolution: {integrity: sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==}
|
||||
engines: {node: '>=6.14.2'}
|
||||
|
||||
utf8-bytes@0.0.1:
|
||||
resolution: {integrity: sha512-GifWmJAx2qAXT+lZLhbkWhBsy7pr6xWHiPWlVToDiELdWgZwt4Ogjf9tlgvKuALzTFR/d+EPQQI9ogJV3957Jg==}
|
||||
|
||||
|
@ -5849,6 +5911,9 @@ packages:
|
|||
webext-bridge@6.0.1:
|
||||
resolution: {integrity: sha512-GruIrN+vNwbxVCi8UW4Dqk5YkcGA9V0ZfJ57jXP9JXHbrsDs5k2N6NNYQR5e+wSCnQpGYOGAGihwUpKlhg8QIw==}
|
||||
|
||||
webextension-polyfill@0.10.0:
|
||||
resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==}
|
||||
|
||||
webextension-polyfill@0.12.0:
|
||||
resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==}
|
||||
|
||||
|
@ -5941,6 +6006,18 @@ packages:
|
|||
write-file-atomic@3.0.3:
|
||||
resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}
|
||||
|
||||
ws@8.17.1:
|
||||
resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
ws@8.18.0:
|
||||
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
@ -5969,6 +6046,10 @@ packages:
|
|||
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
||||
xmlhttprequest-ssl@2.1.1:
|
||||
resolution: {integrity: sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -7186,7 +7267,7 @@ snapshots:
|
|||
uint8arraylist: 2.4.8
|
||||
uint8arrays: 5.1.0
|
||||
|
||||
'@libp2p/websockets@8.2.0':
|
||||
'@libp2p/websockets@8.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
|
||||
dependencies:
|
||||
'@libp2p/interface': 1.7.0
|
||||
'@libp2p/utils': 5.4.9
|
||||
|
@ -7194,12 +7275,12 @@ snapshots:
|
|||
'@multiformats/multiaddr': 12.3.1
|
||||
'@multiformats/multiaddr-to-uri': 10.1.0
|
||||
'@types/ws': 8.5.12
|
||||
it-ws: 6.1.5
|
||||
it-ws: 6.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
p-defer: 4.0.1
|
||||
progress-events: 1.0.1
|
||||
race-signal: 1.1.0
|
||||
wherearewe: 2.0.1
|
||||
ws: 8.18.0
|
||||
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
@ -7781,6 +7862,21 @@ snapshots:
|
|||
'@rollup/rollup-win32-x64-msvc@4.21.3':
|
||||
optional: true
|
||||
|
||||
'@rtco/client@0.2.17':
|
||||
dependencies:
|
||||
'@rtco/peer': 0.2.17
|
||||
bufferutil: 4.0.8
|
||||
eventemitter3: 5.0.1
|
||||
nanoid: 5.0.7
|
||||
socket.io-client: 4.8.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
utf-8-validate: 6.0.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@rtco/peer@0.2.17':
|
||||
dependencies:
|
||||
eventemitter3: 5.0.1
|
||||
|
||||
'@sec-ant/readable-stream@0.4.1': {}
|
||||
|
||||
'@sindresorhus/fnv1a@3.1.0': {}
|
||||
|
@ -7789,6 +7885,8 @@ snapshots:
|
|||
|
||||
'@sindresorhus/merge-streams@4.0.0': {}
|
||||
|
||||
'@socket.io/component-emitter@3.1.2': {}
|
||||
|
||||
'@supabase/auth-js@2.65.0':
|
||||
dependencies:
|
||||
'@supabase/node-fetch': 2.6.15
|
||||
|
@ -7805,12 +7903,12 @@ snapshots:
|
|||
dependencies:
|
||||
'@supabase/node-fetch': 2.6.15
|
||||
|
||||
'@supabase/realtime-js@2.10.2':
|
||||
'@supabase/realtime-js@2.10.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
|
||||
dependencies:
|
||||
'@supabase/node-fetch': 2.6.15
|
||||
'@types/phoenix': 1.6.5
|
||||
'@types/ws': 8.5.12
|
||||
ws: 8.18.0
|
||||
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
@ -7819,13 +7917,13 @@ snapshots:
|
|||
dependencies:
|
||||
'@supabase/node-fetch': 2.6.15
|
||||
|
||||
'@supabase/supabase-js@2.45.4':
|
||||
'@supabase/supabase-js@2.45.4(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
|
||||
dependencies:
|
||||
'@supabase/auth-js': 2.65.0
|
||||
'@supabase/functions-js': 2.4.1
|
||||
'@supabase/node-fetch': 2.6.15
|
||||
'@supabase/postgrest-js': 1.16.1
|
||||
'@supabase/realtime-js': 2.10.2
|
||||
'@supabase/realtime-js': 2.10.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
'@supabase/storage-js': 2.7.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
|
@ -8213,13 +8311,13 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@waku/sdk@0.0.26(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20)':
|
||||
'@waku/sdk@0.0.26(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20)(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
|
||||
dependencies:
|
||||
'@chainsafe/libp2p-noise': 14.1.0
|
||||
'@libp2p/identify': 1.0.21
|
||||
'@libp2p/mplex': 10.1.5
|
||||
'@libp2p/ping': 1.1.6
|
||||
'@libp2p/websockets': 8.2.0
|
||||
'@libp2p/websockets': 8.2.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
'@noble/hashes': 1.5.0
|
||||
'@waku/core': 0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4)
|
||||
'@waku/discovery': 0.0.3(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20)
|
||||
|
@ -8267,6 +8365,17 @@ snapshots:
|
|||
|
||||
'@webext-core/match-patterns@1.0.3': {}
|
||||
|
||||
'@webext-core/messaging@1.4.0':
|
||||
dependencies:
|
||||
serialize-error: 11.0.3
|
||||
webextension-polyfill: 0.10.0
|
||||
|
||||
'@webext-core/proxy-service@1.2.0(@webext-core/messaging@1.4.0)(webextension-polyfill@0.12.0)':
|
||||
dependencies:
|
||||
'@webext-core/messaging': 1.4.0
|
||||
get-value: 3.0.1
|
||||
webextension-polyfill: 0.12.0
|
||||
|
||||
'@xobotyi/scrollbar-width@1.9.5': {}
|
||||
|
||||
JSONStream@1.3.5:
|
||||
|
@ -8482,6 +8591,10 @@ snapshots:
|
|||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
bufferutil@4.0.8:
|
||||
dependencies:
|
||||
node-gyp-build: 4.8.2
|
||||
|
||||
bundle-name@3.0.0:
|
||||
dependencies:
|
||||
run-applescript: 5.0.0
|
||||
|
@ -9011,6 +9124,20 @@ snapshots:
|
|||
dependencies:
|
||||
once: 1.4.0
|
||||
|
||||
engine.io-client@6.6.1(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.2
|
||||
debug: 4.3.7
|
||||
engine.io-parser: 5.2.3
|
||||
ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
xmlhttprequest-ssl: 2.1.1
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
engine.io-parser@5.2.3: {}
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
env-paths@2.2.1: {}
|
||||
|
@ -9669,6 +9796,10 @@ snapshots:
|
|||
es-errors: 1.3.0
|
||||
get-intrinsic: 1.2.4
|
||||
|
||||
get-value@3.0.1:
|
||||
dependencies:
|
||||
isobject: 3.0.1
|
||||
|
||||
giget@1.2.3:
|
||||
dependencies:
|
||||
citty: 0.1.6
|
||||
|
@ -10209,13 +10340,13 @@ snapshots:
|
|||
|
||||
it-take@3.0.6: {}
|
||||
|
||||
it-ws@6.1.5:
|
||||
it-ws@6.1.5(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
dependencies:
|
||||
'@types/ws': 8.5.12
|
||||
event-iterator: 2.0.0
|
||||
it-stream-types: 2.0.2
|
||||
uint8arrays: 5.1.0
|
||||
ws: 8.18.0
|
||||
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
@ -10932,7 +11063,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
mqtt@5.10.1:
|
||||
mqtt@5.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
dependencies:
|
||||
'@types/readable-stream': 4.0.15
|
||||
'@types/ws': 8.5.12
|
||||
|
@ -10949,7 +11080,7 @@ snapshots:
|
|||
rfdc: 1.4.1
|
||||
split2: 4.2.0
|
||||
worker-timers: 7.1.8
|
||||
ws: 8.18.0
|
||||
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
|
@ -11044,6 +11175,8 @@ snapshots:
|
|||
|
||||
node-forge@1.3.1: {}
|
||||
|
||||
node-gyp-build@4.8.2: {}
|
||||
|
||||
node-notifier@10.0.1:
|
||||
dependencies:
|
||||
growly: 1.3.0
|
||||
|
@ -11863,6 +11996,10 @@ snapshots:
|
|||
|
||||
semver@7.6.3: {}
|
||||
|
||||
serialize-error@11.0.3:
|
||||
dependencies:
|
||||
type-fest: 2.19.0
|
||||
|
||||
serialize-error@9.1.1:
|
||||
dependencies:
|
||||
type-fest: 2.19.0
|
||||
|
@ -11945,6 +12082,24 @@ snapshots:
|
|||
ansi-styles: 6.2.1
|
||||
is-fullwidth-code-point: 5.0.0
|
||||
|
||||
socket.io-client@4.8.0(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.2
|
||||
debug: 4.3.7
|
||||
engine.io-client: 6.6.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
socket.io-parser: 4.2.4
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
socket.io-parser@4.2.4:
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.2
|
||||
debug: 4.3.7
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
sonner@1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
|
@ -12246,16 +12401,16 @@ snapshots:
|
|||
|
||||
trough@2.2.0: {}
|
||||
|
||||
trystero@0.20.0(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/proto@0.0.7)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20):
|
||||
trystero@0.20.0(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/proto@0.0.7)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20)(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
dependencies:
|
||||
'@noble/curves': 1.6.0
|
||||
'@supabase/supabase-js': 2.45.4
|
||||
'@supabase/supabase-js': 2.45.4(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
'@thaunknown/simple-peer': 10.0.10
|
||||
'@waku/discovery': 0.0.3(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20)
|
||||
'@waku/sdk': 0.0.26(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20)
|
||||
'@waku/sdk': 0.0.26(@libp2p/interface@1.7.0)(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/enr@0.0.21)(@waku/interfaces@0.0.22)(@waku/message-hash@0.1.16)(@waku/relay@0.0.11(@waku/core@0.0.27(@multiformats/multiaddr@12.3.1)(libp2p@1.9.4))(@waku/interfaces@0.0.22)(@waku/proto@0.0.7)(@waku/utils@0.0.20))(@waku/utils@0.0.20)(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
firebase: 10.13.1
|
||||
libp2p: 1.9.4
|
||||
mqtt: 5.10.1
|
||||
mqtt: 5.10.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- '@libp2p/bootstrap'
|
||||
- '@libp2p/interface'
|
||||
|
@ -12541,6 +12696,10 @@ snapshots:
|
|||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
utf-8-validate@6.0.4:
|
||||
dependencies:
|
||||
node-gyp-build: 4.8.2
|
||||
|
||||
utf8-bytes@0.0.1: {}
|
||||
|
||||
utf8-codec@1.0.0: {}
|
||||
|
@ -12614,7 +12773,7 @@ snapshots:
|
|||
ms: 3.0.0-canary.1
|
||||
supports-color: 9.4.0
|
||||
|
||||
web-ext-run@0.2.1:
|
||||
web-ext-run@0.2.1(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.7
|
||||
'@devicefarmer/adbkit': 3.2.6
|
||||
|
@ -12638,7 +12797,7 @@ snapshots:
|
|||
tmp: 0.2.3
|
||||
update-notifier: 6.0.2
|
||||
watchpack: 2.4.1
|
||||
ws: 8.18.0
|
||||
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
zip-dir: 2.0.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
|
@ -12653,6 +12812,8 @@ snapshots:
|
|||
tiny-uid: 1.1.2
|
||||
webextension-polyfill: 0.9.0
|
||||
|
||||
webextension-polyfill@0.10.0: {}
|
||||
|
||||
webextension-polyfill@0.12.0: {}
|
||||
|
||||
webextension-polyfill@0.9.0: {}
|
||||
|
@ -12768,9 +12929,17 @@ snapshots:
|
|||
signal-exit: 3.0.7
|
||||
typedarray-to-buffer: 3.1.5
|
||||
|
||||
ws@8.18.0: {}
|
||||
ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
optionalDependencies:
|
||||
bufferutil: 4.0.8
|
||||
utf-8-validate: 6.0.4
|
||||
|
||||
wxt@0.19.9(@types/node@22.5.5)(rollup@4.21.3):
|
||||
ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4):
|
||||
optionalDependencies:
|
||||
bufferutil: 4.0.8
|
||||
utf-8-validate: 6.0.4
|
||||
|
||||
wxt@0.19.9(@types/node@22.5.5)(bufferutil@4.0.8)(rollup@4.21.3)(utf-8-validate@6.0.4):
|
||||
dependencies:
|
||||
'@aklinker1/rollup-plugin-visualizer': 5.12.0(rollup@4.21.3)
|
||||
'@types/chrome': 0.0.269
|
||||
|
@ -12814,7 +12983,7 @@ snapshots:
|
|||
unimport: 3.12.0(rollup@4.21.3)
|
||||
vite: 5.4.5(@types/node@22.5.5)
|
||||
vite-node: 2.1.1(@types/node@22.5.5)
|
||||
web-ext-run: 0.2.1
|
||||
web-ext-run: 0.2.1(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
webextension-polyfill: 0.12.0
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
|
@ -12840,6 +13009,8 @@ snapshots:
|
|||
|
||||
xmlbuilder@11.0.1: {}
|
||||
|
||||
xmlhttprequest-ssl@2.1.1: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
||||
yallist@3.1.1: {}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { EVENT } from '@/constants/event'
|
||||
import { browser } from 'wxt/browser'
|
||||
import { defineBackground } from 'wxt/sandbox'
|
||||
|
||||
|
@ -7,8 +8,10 @@ export default defineBackground({
|
|||
type: 'module',
|
||||
|
||||
main() {
|
||||
browser.runtime.onMessage.addListener(async () => {
|
||||
browser.runtime.onMessage.addListener(async (event: EVENT) => {
|
||||
if (event === EVENT.OPEN_OPTIONS_PAGE) {
|
||||
browser.runtime.openOptionsPage()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -5,15 +5,12 @@ import AppButton from '@/app/content/views/AppButton'
|
|||
import AppContainer from '@/app/content/views/AppContainer'
|
||||
import { useRemeshDomain, useRemeshQuery, useRemeshSend } from 'remesh-react'
|
||||
import RoomDomain from '@/domain/Room'
|
||||
import { stringToHex } from '@/utils'
|
||||
import { Toaster } from '@/components/ui/Sonner'
|
||||
import UserInfoDomain from '@/domain/UserInfo'
|
||||
import Setup from '@/app/content/views/Setup'
|
||||
import MessageListDomain from '@/domain/MessageList'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const hostRoomId = stringToHex(document.location.host)
|
||||
|
||||
export default function App() {
|
||||
const send = useRemeshSend()
|
||||
const roomDomain = useRemeshDomain(RoomDomain())
|
||||
|
@ -29,7 +26,7 @@ export default function App() {
|
|||
useEffect(() => {
|
||||
if (userInfoFinished) {
|
||||
if (userInfo) {
|
||||
!roomFinished && send(roomDomain.command.JoinRoomCommand(hostRoomId))
|
||||
!roomFinished && send(roomDomain.command.JoinRoomCommand())
|
||||
} else {
|
||||
send(messageListDomain.command.ClearListCommand())
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export interface PromptItemProps {
|
|||
|
||||
const PromptItem: FC<PromptItemProps> = ({ data, className }) => {
|
||||
return (
|
||||
<div className={cn('flex justify-center py-1', className)}>
|
||||
<div className={cn('flex justify-center py-1 px-4', className)}>
|
||||
<Badge variant="secondary" className="gap-x-2 rounded-full font-medium text-slate-400">
|
||||
<Avatar className="size-4">
|
||||
<AvatarImage src={data.userAvatar} alt="avatar" />
|
||||
|
|
|
@ -8,7 +8,8 @@ import { createShadowRootUi } from 'wxt/client'
|
|||
|
||||
import App from './App'
|
||||
import { IndexDBStorageImpl, BrowserSyncStorageImpl } from '@/domain/impls/Storage'
|
||||
import { PeerRoomImpl } from '@/domain/impls/PeerRoom'
|
||||
// import { PeerRoomImpl } from '@/domain/impls/PeerRoom'
|
||||
import { PeerRoomImpl } from '@/domain/impls/PeerRoom2'
|
||||
import '@/assets/styles/tailwind.css'
|
||||
import { createElement } from '@/utils'
|
||||
import { ToastImpl } from '@/domain/impls/Toast'
|
||||
|
@ -19,7 +20,7 @@ export default defineContentScript({
|
|||
async main(ctx) {
|
||||
const store = Remesh.store({
|
||||
externs: [IndexDBStorageImpl, BrowserSyncStorageImpl, PeerRoomImpl, ToastImpl],
|
||||
inspectors: !__DEV__ ? [RemeshLogger()] : []
|
||||
inspectors: __DEV__ ? [RemeshLogger()] : []
|
||||
})
|
||||
|
||||
const ui = await createShadowRootUi(ctx, {
|
||||
|
|
|
@ -6,13 +6,12 @@ import { Button } from '@/components/ui/Button'
|
|||
import { getSiteInfo } from '@/utils'
|
||||
import { useRemeshDomain, useRemeshQuery } from 'remesh-react'
|
||||
import RoomDomain from '@/domain/Room'
|
||||
import { selfId } from 'trystero'
|
||||
|
||||
const Header: FC = () => {
|
||||
const siteInfo = getSiteInfo()
|
||||
const roomDomain = useRemeshDomain(RoomDomain())
|
||||
const userList = useRemeshQuery(roomDomain.query.UserListQuery())
|
||||
console.log('userList', [...userList], userList.length)
|
||||
const peerId = useRemeshQuery(roomDomain.query.PeerIdQuery())
|
||||
|
||||
return (
|
||||
<div className="z-10 grid h-12 grid-flow-col items-center justify-between gap-x-4 rounded-t-xl bg-white px-4 backdrop-blur-lg">
|
||||
|
@ -27,7 +26,7 @@ const Header: FC = () => {
|
|||
<Button className="overflow-hidden" variant="link">
|
||||
<span className="truncate text-lg font-medium text-slate-600">
|
||||
{/* {siteInfo.hostname.replace(/^www\./i, '')} */}
|
||||
{selfId}
|
||||
{peerId}
|
||||
</span>
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
|
|
|
@ -21,8 +21,8 @@ const AvatarSelect = React.forwardRef<HTMLInputElement, AvatarSelectProps>(
|
|||
const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0]
|
||||
if (file) {
|
||||
if (!/image\/(png|jpeg)/.test(file.type)) {
|
||||
onWarning?.(new Error('Only PNG and JPEG image are supported.'))
|
||||
if (!/image\/(png|jpeg|webp)/.test(file.type)) {
|
||||
onWarning?.(new Error('Only PNG, JPEG and WebP image are supported.'))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ const formSchema = v.object({
|
|||
avatar: v.pipe(
|
||||
v.string(),
|
||||
v.notLength(0, 'Please select your avatar.'),
|
||||
v.maxBytes(8 * 1024, 'Your avatar cannot exceed 8kb.')
|
||||
v.maxBytes(8 * 1024, `Your avatar cannot exceed 8kb.`)
|
||||
),
|
||||
themeMode: v.pipe(
|
||||
v.string(),
|
||||
|
@ -92,9 +92,8 @@ const ProfileForm = () => {
|
|||
control={form.control}
|
||||
name="avatar"
|
||||
render={({ field }) => (
|
||||
<FormItem className="absolute left-1/2 top-0 grid -translate-x-1/2 -translate-y-1/2 justify-items-center">
|
||||
<FormItem className="absolute inset-x-1 top-0 mx-auto grid w-fit -translate-y-1/2 justify-items-center">
|
||||
<FormControl>
|
||||
<div className="grid justify-items-center gap-y-2">
|
||||
<AvatarSelect
|
||||
compressSize={MAX_AVATAR_SIZE}
|
||||
onError={handleError}
|
||||
|
@ -102,22 +101,15 @@ const ProfileForm = () => {
|
|||
className="shadow-lg"
|
||||
{...field}
|
||||
></AvatarSelect>
|
||||
<Button
|
||||
type="button"
|
||||
size="xs"
|
||||
className="mx-auto flex items-center gap-x-2"
|
||||
onClick={handleRefreshAvatar}
|
||||
>
|
||||
<RefreshCcwIcon size={14} />
|
||||
Ugly Avatar
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button type="button" size="xs" className="mx-auto flex items-center gap-x-2" onClick={handleRefreshAvatar}>
|
||||
<RefreshCcwIcon size={14} />
|
||||
Ugly Avatar
|
||||
</Button>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
|
|
|
@ -194,4 +194,4 @@ export const STORAGE_NAME = 'WEB_CHAT' as const
|
|||
* Image is encoded as base64, and the size is increased by about 33%.
|
||||
* 8kb * (1 - 0.33) = 5488 bytes
|
||||
*/
|
||||
export const MAX_AVATAR_SIZE = 5488 as const
|
||||
export const MAX_AVATAR_SIZE = 5120 as const
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Remesh } from 'remesh'
|
||||
import { map, merge, switchMap, tap, of, EMPTY, mergeMap } from 'rxjs'
|
||||
import { map, merge, of, EMPTY, mergeMap } from 'rxjs'
|
||||
import { NormalMessage, type MessageUser } from './MessageList'
|
||||
import { PeerRoomExtern } from '@/domain/externs/PeerRoom'
|
||||
import MessageListDomain, { MessageType } from '@/domain/MessageList'
|
||||
|
@ -51,6 +51,18 @@ const RoomDomain = Remesh.domain({
|
|||
const userInfoDomain = domain.getDomain(UserInfoDomain())
|
||||
const peerRoom = domain.getExtern(PeerRoomExtern)
|
||||
|
||||
const PeerIdState = domain.state<string>({
|
||||
name: 'Room.PeerIdState',
|
||||
default: peerRoom.peerId
|
||||
})
|
||||
|
||||
const PeerIdQuery = domain.query({
|
||||
name: 'Room.PeerIdQuery',
|
||||
impl: ({ get }) => {
|
||||
return get(PeerIdState())
|
||||
}
|
||||
})
|
||||
|
||||
const MessageListQuery = messageListDomain.query.ListQuery
|
||||
|
||||
const RoomStatusModule = StatusModule(domain, {
|
||||
|
@ -71,16 +83,17 @@ const RoomDomain = Remesh.domain({
|
|||
|
||||
const JoinRoomCommand = domain.command({
|
||||
name: 'RoomJoinRoomCommand',
|
||||
impl: ({ get }, roomId: string) => {
|
||||
peerRoom.joinRoom(roomId)
|
||||
impl: ({ get }) => {
|
||||
peerRoom.joinRoom()
|
||||
const { id: userId, name: username, avatar: userAvatar } = get(userInfoDomain.query.UserInfoQuery())!
|
||||
|
||||
return [
|
||||
JoinRoomEvent(roomId),
|
||||
RoomStatusModule.command.SetFinishedCommand(),
|
||||
UpdateUserListCommand({
|
||||
type: 'create',
|
||||
user: { peerId: peerRoom.selfId, joinTime: Date.now(), userId, username, userAvatar }
|
||||
})
|
||||
user: { peerId: peerRoom.peerId, joinTime: Date.now(), userId, username, userAvatar }
|
||||
}),
|
||||
RoomStatusModule.command.SetFinishedCommand(),
|
||||
JoinRoomEvent(peerRoom.roomId)
|
||||
]
|
||||
}
|
||||
})
|
||||
|
@ -91,12 +104,12 @@ const RoomDomain = Remesh.domain({
|
|||
peerRoom.leaveRoom()
|
||||
const { id: userId, name: username, avatar: userAvatar } = get(userInfoDomain.query.UserInfoQuery())!
|
||||
return [
|
||||
LeaveRoomEvent(roomId),
|
||||
RoomStatusModule.command.SetInitialCommand(),
|
||||
UpdateUserListCommand({
|
||||
type: 'delete',
|
||||
user: { peerId: peerRoom.selfId, joinTime: Date.now(), userId, username, userAvatar }
|
||||
})
|
||||
user: { peerId: peerRoom.peerId, joinTime: Date.now(), userId, username, userAvatar }
|
||||
}),
|
||||
RoomStatusModule.command.SetInitialCommand(),
|
||||
LeaveRoomEvent(roomId)
|
||||
]
|
||||
}
|
||||
})
|
||||
|
@ -105,22 +118,26 @@ const RoomDomain = Remesh.domain({
|
|||
name: 'RoomSendTextMessageCommand',
|
||||
impl: ({ get }, message: string) => {
|
||||
const { id: userId, name: username, avatar: userAvatar } = get(userInfoDomain.query.UserInfoQuery())!
|
||||
const id = nanoid()
|
||||
const date = Date.now()
|
||||
return [
|
||||
messageListDomain.command.CreateItemCommand({
|
||||
id,
|
||||
type: MessageType.Normal,
|
||||
|
||||
const textMessage: TextMessage = {
|
||||
id: nanoid(),
|
||||
type: SendType.Text,
|
||||
body: message,
|
||||
date,
|
||||
userId,
|
||||
username,
|
||||
userAvatar,
|
||||
userAvatar
|
||||
}
|
||||
|
||||
const listMessage: NormalMessage = {
|
||||
...textMessage,
|
||||
type: MessageType.Normal,
|
||||
date: Date.now(),
|
||||
likeUsers: [],
|
||||
hateUsers: []
|
||||
}),
|
||||
SendTextMessageEvent({ id, body: message, userId, username, userAvatar, type: SendType.Text })
|
||||
]
|
||||
}
|
||||
|
||||
peerRoom.sendMessage<RoomMessage>(textMessage)
|
||||
return [messageListDomain.command.CreateItemCommand(listMessage), SendTextMessageEvent(textMessage)]
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -128,28 +145,23 @@ const RoomDomain = Remesh.domain({
|
|||
name: 'RoomSendLikeMessageCommand',
|
||||
impl: ({ get }, messageId: string) => {
|
||||
const { id: userId, name: username, avatar: userAvatar } = get(userInfoDomain.query.UserInfoQuery())!
|
||||
const _message = get(messageListDomain.query.ItemQuery(messageId)) as NormalMessage
|
||||
return [
|
||||
messageListDomain.command.UpdateItemCommand({
|
||||
..._message,
|
||||
likeUsers: desert(
|
||||
_message.likeUsers,
|
||||
{
|
||||
userId,
|
||||
username,
|
||||
userAvatar
|
||||
},
|
||||
'userId'
|
||||
)
|
||||
}),
|
||||
SendLikeMessageEvent({
|
||||
const localMessage = get(messageListDomain.query.ItemQuery(messageId)) as NormalMessage
|
||||
|
||||
const likeMessage: LikeMessage = {
|
||||
id: messageId,
|
||||
userId,
|
||||
username,
|
||||
userAvatar,
|
||||
type: SendType.Like
|
||||
})
|
||||
]
|
||||
}
|
||||
const listMessage: NormalMessage = {
|
||||
...localMessage,
|
||||
type: MessageType.Normal,
|
||||
date: Date.now(),
|
||||
likeUsers: desert(localMessage.likeUsers, likeMessage, 'userId')
|
||||
}
|
||||
peerRoom.sendMessage<RoomMessage>(likeMessage)
|
||||
return [messageListDomain.command.UpdateItemCommand(listMessage), SendLikeMessageEvent(likeMessage)]
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -157,47 +169,55 @@ const RoomDomain = Remesh.domain({
|
|||
name: 'RoomSendHateMessageCommand',
|
||||
impl: ({ get }, messageId: string) => {
|
||||
const { id: userId, name: username, avatar: userAvatar } = get(userInfoDomain.query.UserInfoQuery())!
|
||||
const _message = get(messageListDomain.query.ItemQuery(messageId)) as NormalMessage
|
||||
const localMessage = get(messageListDomain.query.ItemQuery(messageId)) as NormalMessage
|
||||
|
||||
return [
|
||||
messageListDomain.command.UpdateItemCommand({
|
||||
..._message,
|
||||
hateUsers: desert(
|
||||
_message.hateUsers,
|
||||
{
|
||||
const hateMessage: HateMessage = {
|
||||
id: messageId,
|
||||
userId,
|
||||
username,
|
||||
userAvatar
|
||||
},
|
||||
'userId'
|
||||
)
|
||||
}),
|
||||
SendHateMessageEvent({ id: messageId, userId, username, userAvatar, type: SendType.Hate })
|
||||
]
|
||||
userAvatar,
|
||||
type: SendType.Hate
|
||||
}
|
||||
const listMessage: NormalMessage = {
|
||||
...localMessage,
|
||||
type: MessageType.Normal,
|
||||
date: Date.now(),
|
||||
hateUsers: desert(localMessage.hateUsers, hateMessage, 'userId')
|
||||
}
|
||||
peerRoom.sendMessage<RoomMessage>(hateMessage)
|
||||
return [messageListDomain.command.UpdateItemCommand(listMessage), SendHateMessageEvent(hateMessage)]
|
||||
}
|
||||
})
|
||||
|
||||
const SendUserSyncMessageCommand = domain.command({
|
||||
name: 'RoomSendUserSyncMessageCommand',
|
||||
impl: ({ get }, targetPeerId: string) => {
|
||||
const { id: userId, name: username, avatar: userAvatar } = get(userInfoDomain.query.UserInfoQuery())!
|
||||
const joinTime = get(UserListQuery()).find((u) => u.peerId === peerRoom.selfId)?.joinTime || Date.now()
|
||||
return [
|
||||
SendUserSyncMessageEvent({
|
||||
const self = get(UserListQuery()).find((user) => user.peerId === peerRoom.peerId)!
|
||||
|
||||
const syncUserMessage: SyncUserMessage = {
|
||||
...self,
|
||||
id: nanoid(),
|
||||
peerId: peerRoom.selfId,
|
||||
targetPeerId,
|
||||
userId,
|
||||
joinTime,
|
||||
username,
|
||||
userAvatar,
|
||||
type: SendType.UserSync
|
||||
})
|
||||
]
|
||||
}
|
||||
|
||||
peerRoom.sendMessage<RoomMessage>(syncUserMessage, targetPeerId)
|
||||
return [SendUserSyncMessageEvent(syncUserMessage)]
|
||||
}
|
||||
})
|
||||
|
||||
const SendUserSyncMessageEvent = domain.event<SyncUserMessage & { targetPeerId: string }>({
|
||||
const UpdateUserListCommand = domain.command({
|
||||
name: 'RoomUpdateUserListCommand',
|
||||
impl: ({ get }, action: { type: 'create' | 'delete'; user: RoomUser }) => {
|
||||
const userList = get(UserListState())
|
||||
if (action.type === 'create') {
|
||||
return [UserListState().new(upsert(userList, action.user, 'peerId'))]
|
||||
} else {
|
||||
return [UserListState().new(userList.filter(({ peerId }) => peerId !== action.user.peerId))]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const SendUserSyncMessageEvent = domain.event<SyncUserMessage>({
|
||||
name: 'RoomSendUserSyncMessageEvent'
|
||||
})
|
||||
|
||||
|
@ -233,94 +253,47 @@ const RoomDomain = Remesh.domain({
|
|||
name: 'RoomOnLeaveRoomEvent'
|
||||
})
|
||||
|
||||
const UpdateUserListCommand = domain.command({
|
||||
name: 'RoomUpdateUserListCommand',
|
||||
impl: ({ get }, action: { type: 'create' | 'delete'; user: RoomUser }) => {
|
||||
const userList = get(UserListState())
|
||||
if (action.type === 'create') {
|
||||
return [UserListState().new(upsert(userList, action.user, 'peerId'))]
|
||||
domain.effect({
|
||||
name: 'RoomOnJoinRoomEffect',
|
||||
impl: () => {
|
||||
const onJoinRoom$ = callbackToObservable<string>(peerRoom.onJoinRoom).pipe(
|
||||
mergeMap((peerId) => {
|
||||
console.log('onJoinRoom', peerId)
|
||||
if (peerRoom.peerId === peerId) {
|
||||
return [OnJoinRoomEvent(peerId)]
|
||||
} else {
|
||||
return [UserListState().new(userList.filter(({ peerId }) => peerId !== action.user.peerId))]
|
||||
return [SendUserSyncMessageCommand(peerId), OnJoinRoomEvent(peerId)]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
domain.effect({
|
||||
name: 'RoomSendTextMessageEffect',
|
||||
impl: ({ fromEvent }) => {
|
||||
const sendMessage$ = fromEvent(SendTextMessageEvent).pipe(
|
||||
tap(async (message) => {
|
||||
peerRoom.sendMessage<RoomMessage>(message)
|
||||
})
|
||||
)
|
||||
return merge(sendMessage$).pipe(map(() => null))
|
||||
}
|
||||
})
|
||||
|
||||
domain.effect({
|
||||
name: 'RoomSendLikeMessageEffect',
|
||||
impl: ({ fromEvent }) => {
|
||||
const likeMessage$ = fromEvent(SendLikeMessageEvent).pipe(
|
||||
tap(async (message) => {
|
||||
return peerRoom.sendMessage<RoomMessage>(message)
|
||||
})
|
||||
)
|
||||
return merge(likeMessage$).pipe(map(() => null))
|
||||
}
|
||||
})
|
||||
|
||||
domain.effect({
|
||||
name: 'RoomSendHateMessageEffect',
|
||||
impl: ({ fromEvent }) => {
|
||||
const hateMessage$ = fromEvent(SendHateMessageEvent).pipe(
|
||||
tap(async (message) => {
|
||||
peerRoom.sendMessage<RoomMessage>(message)
|
||||
})
|
||||
)
|
||||
return merge(hateMessage$).pipe(map(() => null))
|
||||
}
|
||||
})
|
||||
|
||||
domain.effect({
|
||||
name: 'RoomSendUserSyncMessageEffect',
|
||||
impl: ({ fromEvent }) => {
|
||||
const userSyncMessage$ = fromEvent(SendUserSyncMessageEvent).pipe(
|
||||
tap(async (message) => {
|
||||
console.log('sendMessage', message)
|
||||
|
||||
peerRoom.sendMessage<RoomMessage>(message, message.targetPeerId)
|
||||
})
|
||||
)
|
||||
return merge(userSyncMessage$).pipe(map(() => null))
|
||||
return onJoinRoom$
|
||||
}
|
||||
})
|
||||
|
||||
domain.effect({
|
||||
name: 'RoomOnMessageEffect',
|
||||
impl: ({ fromEvent, get }) => {
|
||||
const onMessage$ = fromEvent(JoinRoomEvent).pipe(
|
||||
switchMap(() => callbackToObservable<RoomMessage>(peerRoom.onMessage.bind(peerRoom))),
|
||||
impl: ({ get }) => {
|
||||
const onMessage$ = callbackToObservable<RoomMessage>(peerRoom.onMessage).pipe(
|
||||
mergeMap((message) => {
|
||||
console.log('onMessage', message)
|
||||
|
||||
const messageEvent$ = of(OnMessageEvent(message))
|
||||
|
||||
const commandEvent$ = (() => {
|
||||
switch (message.type) {
|
||||
case SendType.UserSync: {
|
||||
const self = get(UserListQuery()).find((user) => user.peerId === peerRoom.selfId)!
|
||||
if (self.joinTime > message.joinTime) {
|
||||
return EMPTY
|
||||
}
|
||||
const self = get(UserListQuery()).find((user) => user.peerId === peerRoom.peerId)!
|
||||
const isJoining = self.joinTime < message.joinTime
|
||||
return of(
|
||||
UpdateUserListCommand({ type: 'create', user: message }),
|
||||
messageListDomain.command.CreateItemCommand({
|
||||
isJoining
|
||||
? messageListDomain.command.CreateItemCommand({
|
||||
...message,
|
||||
id: nanoid(),
|
||||
body: `"${message.username}" joined the chat`,
|
||||
type: MessageType.Prompt,
|
||||
date: Date.now()
|
||||
})
|
||||
: null
|
||||
)
|
||||
}
|
||||
case SendType.Text:
|
||||
|
@ -356,7 +329,7 @@ const RoomDomain = Remesh.domain({
|
|||
)
|
||||
}
|
||||
default:
|
||||
console.warn('未知消息类型', message)
|
||||
console.warn('Unsupported message type', message)
|
||||
return EMPTY
|
||||
}
|
||||
})()
|
||||
|
@ -367,36 +340,14 @@ const RoomDomain = Remesh.domain({
|
|||
}
|
||||
})
|
||||
|
||||
domain.effect({
|
||||
name: 'RoomOnJoinRoomEffect',
|
||||
impl: ({ fromEvent, get }) => {
|
||||
const onJoinRoom$ = fromEvent(JoinRoomEvent).pipe(
|
||||
switchMap(() => callbackToObservable<string>(peerRoom.onJoinRoom.bind(peerRoom))),
|
||||
mergeMap((peerId) => {
|
||||
console.log('onJoinRoom', peerId)
|
||||
const { id: userId, name: username, avatar: userAvatar } = get(userInfoDomain.query.UserInfoQuery())!
|
||||
return [
|
||||
SendUserSyncMessageCommand(peerId),
|
||||
UpdateUserListCommand({
|
||||
type: 'create',
|
||||
user: { peerId, joinTime: Date.now(), userId, username, userAvatar }
|
||||
}),
|
||||
OnJoinRoomEvent(peerId)
|
||||
]
|
||||
})
|
||||
)
|
||||
return onJoinRoom$
|
||||
}
|
||||
})
|
||||
|
||||
domain.effect({
|
||||
name: 'RoomOnLeaveRoomEffect',
|
||||
impl: ({ fromEvent, get }) => {
|
||||
const onLeaveRoom$ = fromEvent(JoinRoomEvent).pipe(
|
||||
switchMap(() => callbackToObservable<string>(peerRoom.onLeaveRoom.bind(peerRoom))),
|
||||
impl: ({ get }) => {
|
||||
const onLeaveRoom$ = callbackToObservable<string>(peerRoom.onLeaveRoom).pipe(
|
||||
map((peerId) => {
|
||||
console.log('onLeaveRoom', peerId)
|
||||
const user = get(UserListQuery()).find((user) => user.peerId === peerId)
|
||||
|
||||
if (user) {
|
||||
return [
|
||||
UpdateUserListCommand({ type: 'delete', user }),
|
||||
|
@ -420,6 +371,7 @@ const RoomDomain = Remesh.domain({
|
|||
|
||||
return {
|
||||
query: {
|
||||
PeerIdQuery,
|
||||
UserListQuery,
|
||||
MessageListQuery,
|
||||
...RoomStatusModule.query
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
import { Remesh } from 'remesh'
|
||||
import { type Promisable } from 'type-fest'
|
||||
|
||||
export type PeerMessage = object | Blob | ArrayBuffer | ArrayBufferView
|
||||
|
||||
export interface PeerRoom {
|
||||
readonly selfId: string
|
||||
joinRoom: (roomId: string) => Promise<any>
|
||||
sendMessage: <T extends PeerMessage>(message: T, id?: string) => Promise<any>
|
||||
onMessage: <T extends PeerMessage>(callback: (message: T) => void) => Promisable<void>
|
||||
leaveRoom: () => Promisable<void>
|
||||
onJoinRoom: (callback: (id: string) => void) => Promisable<void>
|
||||
onLeaveRoom: (callback: (id: string) => void) => Promisable<void>
|
||||
readonly peerId: string
|
||||
readonly roomId: string
|
||||
joinRoom: () => PeerRoom
|
||||
sendMessage: <T extends PeerMessage>(message: T, id?: string) => PeerRoom
|
||||
onMessage: <T extends PeerMessage>(callback: (message: T) => void) => PeerRoom
|
||||
leaveRoom: () => PeerRoom
|
||||
onJoinRoom: (callback: (id: string) => void) => PeerRoom
|
||||
onLeaveRoom: (callback: (id: string) => void) => PeerRoom
|
||||
onError: (callback: (error: Error) => void) => PeerRoom
|
||||
}
|
||||
|
||||
export const PeerRoomExtern = Remesh.extern<PeerRoom>({
|
||||
default: {
|
||||
selfId: '',
|
||||
joinRoom: async () => {
|
||||
peerId: '',
|
||||
roomId: '',
|
||||
joinRoom: () => {
|
||||
throw new Error('"joinRoom" not implemented.')
|
||||
},
|
||||
sendMessage: async () => {
|
||||
sendMessage: () => {
|
||||
throw new Error('"sendMessage" not implemented.')
|
||||
},
|
||||
onMessage: () => {
|
||||
|
@ -33,6 +35,9 @@ export const PeerRoomExtern = Remesh.extern<PeerRoom>({
|
|||
},
|
||||
onLeaveRoom: () => {
|
||||
throw new Error('"onLeaveRoom" not implemented.')
|
||||
},
|
||||
onError: () => {
|
||||
throw new Error('"onError" not implemented.')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,64 +1,151 @@
|
|||
import { type DataPayload, type Room, joinRoom, selfId } from 'trystero'
|
||||
|
||||
// import { joinRoom } from 'trystero/firebase'
|
||||
|
||||
import { PeerRoomExtern, type PeerMessage } from '@/domain/externs/PeerRoom'
|
||||
import { stringToHex } from '@/utils'
|
||||
import EventHub from '@resreq/event-hub'
|
||||
|
||||
export interface Config {
|
||||
appId: string
|
||||
peerId?: string
|
||||
roomId: string
|
||||
}
|
||||
|
||||
class PeerRoom {
|
||||
class PeerRoom extends EventHub {
|
||||
readonly appId: string
|
||||
room: Room | null
|
||||
readonly selfId: string
|
||||
private room?: Room
|
||||
readonly roomId: string
|
||||
readonly peerId: string
|
||||
constructor(config: Config) {
|
||||
this.appId = config.appId
|
||||
this.room = null
|
||||
this.selfId = selfId
|
||||
super()
|
||||
this.appId = __NAME__
|
||||
this.roomId = config.roomId
|
||||
this.peerId = selfId
|
||||
this.joinRoom = this.joinRoom.bind(this)
|
||||
this.sendMessage = this.sendMessage.bind(this)
|
||||
this.onMessage = this.onMessage.bind(this)
|
||||
this.onJoinRoom = this.onJoinRoom.bind(this)
|
||||
this.onLeaveRoom = this.onLeaveRoom.bind(this)
|
||||
this.leaveRoom = this.leaveRoom.bind(this)
|
||||
this.onError = this.onError.bind(this)
|
||||
}
|
||||
|
||||
async joinRoom(roomId: string) {
|
||||
this.room = joinRoom({ appId: this.appId }, roomId)
|
||||
|
||||
return this.room
|
||||
joinRoom() {
|
||||
this.room = joinRoom({ appId: this.appId }, this.roomId)
|
||||
/**
|
||||
* If we wait to join, it will result in not being able to listen to our own join event.
|
||||
* This might be related to the fact that:
|
||||
* (If called more than once, only the latest callback registered is ever called.)
|
||||
* Multiple listeners may overwrite each other.
|
||||
* @see: https://github.com/dmotz/trystero?tab=readme-ov-file#onpeerjoincallback
|
||||
*/
|
||||
// this.room.onPeerJoin(() => this.emit('action'))
|
||||
this.emit('action')
|
||||
return this
|
||||
}
|
||||
|
||||
async sendMessage<T extends PeerMessage>(message: T, id?: string) {
|
||||
sendMessage<T extends PeerMessage>(message: T, id?: string) {
|
||||
if (!this.room) {
|
||||
throw new Error('Room not joined')
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
const [send] = this.room!.makeAction('MESSAGE')
|
||||
return await send(message as DataPayload, id)
|
||||
const [send] = this.room.makeAction('MESSAGE')
|
||||
send(message as DataPayload, id)
|
||||
})
|
||||
} else {
|
||||
const [send] = this.room.makeAction('MESSAGE')
|
||||
send(message as DataPayload, id)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
onMessage<T extends PeerMessage>(callback: (message: T) => void) {
|
||||
if (!this.room) {
|
||||
throw new Error('Room not joined')
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
const [, on] = this.room!.makeAction('MESSAGE')
|
||||
const [, on] = this.room.makeAction('MESSAGE')
|
||||
on((message) => callback(message as T))
|
||||
})
|
||||
} else {
|
||||
const [, on] = this.room.makeAction('MESSAGE')
|
||||
on((message) => callback(message as T))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
onJoinRoom(callback: (id: string) => void) {
|
||||
if (!this.room) {
|
||||
throw new Error('Room not joined')
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.onPeerJoin((peerId) => callback(peerId))
|
||||
this.room.onPeerJoin((peerId) => {
|
||||
callback(peerId)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.room.onPeerJoin((peerId) => {
|
||||
callback(peerId)
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
onLeaveRoom(callback: (id: string) => void) {
|
||||
if (!this.room) {
|
||||
throw new Error('Room not joined')
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.onPeerLeave((peerId) => callback(peerId))
|
||||
})
|
||||
} else {
|
||||
this.room.onPeerLeave((peerId) => callback(peerId))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
async leaveRoom() {
|
||||
return await this.room?.leave()
|
||||
leaveRoom() {
|
||||
if (!this.room) {
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.leave()
|
||||
this.room = undefined
|
||||
})
|
||||
} else {
|
||||
this.room.leave()
|
||||
this.room = undefined
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
onError(callback: (error: Error) => void) {
|
||||
this.on('error', (error: Error) => callback(error))
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
const peerRoom = new PeerRoom({ appId: stringToHex(__NAME__) })
|
||||
const hostRoomId = stringToHex(document.location.host)
|
||||
const peerRoom = new PeerRoom({ roomId: hostRoomId })
|
||||
|
||||
export const PeerRoomImpl = PeerRoomExtern.impl(peerRoom)
|
||||
|
||||
// https://github.com/w3c/webextensions/issues/72
|
||||
// https://issues.chromium.org/issues/40251342
|
||||
// https://github.com/w3c/webrtc-extensions/issues/77
|
||||
|
|
142
src/domain/impls/PeerRoom2.ts
Normal file
142
src/domain/impls/PeerRoom2.ts
Normal file
|
@ -0,0 +1,142 @@
|
|||
import { Artico, Room } from '@rtco/client'
|
||||
|
||||
import { PeerRoomExtern, type PeerMessage } from '@/domain/externs/PeerRoom'
|
||||
import { stringToHex } from '@/utils'
|
||||
import { nanoid } from 'nanoid'
|
||||
import EventHub from '@resreq/event-hub'
|
||||
export interface Config {
|
||||
peerId?: string
|
||||
roomId: string
|
||||
}
|
||||
|
||||
class PeerRoom extends EventHub {
|
||||
readonly roomId: string
|
||||
private rtco?: Artico
|
||||
readonly peerId: string
|
||||
private room?: Room
|
||||
|
||||
constructor(config: Config) {
|
||||
super()
|
||||
this.roomId = config.roomId
|
||||
this.peerId = config.peerId || nanoid()
|
||||
this.joinRoom = this.joinRoom.bind(this)
|
||||
this.sendMessage = this.sendMessage.bind(this)
|
||||
this.onMessage = this.onMessage.bind(this)
|
||||
this.onJoinRoom = this.onJoinRoom.bind(this)
|
||||
this.onLeaveRoom = this.onLeaveRoom.bind(this)
|
||||
this.leaveRoom = this.leaveRoom.bind(this)
|
||||
this.onError = this.onError.bind(this)
|
||||
}
|
||||
|
||||
joinRoom() {
|
||||
if (!this.rtco) {
|
||||
this.rtco = new Artico({ id: this.peerId })
|
||||
}
|
||||
if (this.room) {
|
||||
this.room = this.rtco.join(this.roomId)
|
||||
} else {
|
||||
this.rtco!.on('open', () => {
|
||||
this.room = this.rtco!.join(this.roomId)
|
||||
this.emit('action')
|
||||
})
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
sendMessage<T extends PeerMessage>(message: T, id?: string) {
|
||||
if (!this.room) {
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.send(JSON.stringify(message), id)
|
||||
})
|
||||
} else {
|
||||
this.room.send(JSON.stringify(message), id)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
onMessage<T extends PeerMessage>(callback: (message: T) => void) {
|
||||
if (!this.room) {
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.on('message', (message) => callback(JSON.parse(message) as T))
|
||||
})
|
||||
} else {
|
||||
this.room.on('message', (message) => callback(JSON.parse(message) as T))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
onJoinRoom(callback: (id: string) => void) {
|
||||
if (!this.room) {
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.on('join', (id) => callback(id))
|
||||
})
|
||||
} else {
|
||||
this.room.on('join', (id) => callback(id))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
onLeaveRoom(callback: (id: string) => void) {
|
||||
if (!this.room) {
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.on('leave', (id) => callback(id))
|
||||
})
|
||||
} else {
|
||||
this.room.on('leave', (id) => callback(id))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
leaveRoom() {
|
||||
if (!this.room) {
|
||||
this.once('action', () => {
|
||||
if (!this.room) {
|
||||
const error = new Error('Room not joined')
|
||||
this.emit('error', error)
|
||||
throw error
|
||||
}
|
||||
this.room.leave()
|
||||
this.room = undefined
|
||||
})
|
||||
} else {
|
||||
this.room.leave()
|
||||
this.room = undefined
|
||||
}
|
||||
return this
|
||||
}
|
||||
onError(callback: (error: Error) => void) {
|
||||
this.rtco?.on('error', (error) => callback(error))
|
||||
this.on('error', (error: Error) => callback(error))
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
const hostRoomId = stringToHex(document.location.host)
|
||||
|
||||
const peerRoom = new PeerRoom({ roomId: hostRoomId })
|
||||
|
||||
export const PeerRoomImpl = PeerRoomExtern.impl(peerRoom)
|
||||
|
||||
// https://github.com/w3c/webextensions/issues/72
|
||||
// https://issues.chromium.org/issues/40251342
|
||||
// https://github.com/w3c/webrtc-extensions/issues/77
|
|
@ -1,11 +1,11 @@
|
|||
import generateUglyAvatar from '@/lib/uglyAvatar'
|
||||
import compressImage from './compressImage'
|
||||
|
||||
const generateRandomAvatar = async (idealSize: number) => {
|
||||
const generateRandomAvatar = async (targetSize: number) => {
|
||||
const svgBlob = generateUglyAvatar()
|
||||
|
||||
// compressImage can't directly compress svg, need to convert to jpeg first
|
||||
const jpegBlob = await new Promise<Blob>((resolve, reject) => {
|
||||
const imageBlob = await new Promise<Blob>((resolve, reject) => {
|
||||
const image = new Image()
|
||||
image.onload = async () => {
|
||||
const canvas = new OffscreenCanvas(image.width, image.height)
|
||||
|
@ -17,7 +17,7 @@ const generateRandomAvatar = async (idealSize: number) => {
|
|||
image.onerror = () => reject(new Error('Failed to load SVG'))
|
||||
image.src = URL.createObjectURL(svgBlob)
|
||||
})
|
||||
const miniAvatarBlob = await compressImage({ input: jpegBlob, targetSize: idealSize })
|
||||
const miniAvatarBlob = await compressImage({ input: imageBlob, targetSize })
|
||||
const miniAvatarBase64 = await new Promise<string>((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => resolve(e.target?.result as string)
|
||||
|
|
Loading…
Reference in a new issue