diff --git a/src/app/content/App.tsx b/src/app/content/App.tsx
index 43f02c5..1d1d6e9 100644
--- a/src/app/content/App.tsx
+++ b/src/app/content/App.tsx
@@ -5,11 +5,11 @@ 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 { 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'
+import { Toaster } from 'sonner'
export default function App() {
const send = useRemeshSend()
@@ -23,8 +23,13 @@ export default function App() {
const notUserInfo = userInfoLoadFinished && !userInfoSetFinished
useEffect(() => {
- if (userInfoSetFinished && messageListLoadFinished) {
- send(roomDomain.command.JoinRoomCommand())
+ if (messageListLoadFinished) {
+ if (userInfoSetFinished) {
+ send(roomDomain.command.JoinRoomCommand())
+ } else {
+ // Clear simulated data when refreshing on the setup page
+ send(messageListDomain.command.ClearListCommand())
+ }
}
}, [userInfoSetFinished, messageListLoadFinished])
@@ -35,9 +40,9 @@ export default function App() {
{notUserInfo && }
+
-
>
)
}
diff --git a/src/app/content/index.tsx b/src/app/content/index.tsx
index 96b85ab..aa25661 100644
--- a/src/app/content/index.tsx
+++ b/src/app/content/index.tsx
@@ -11,6 +11,7 @@ import { IndexDBStorageImpl, BrowserSyncStorageImpl } from '@/domain/impls/Stora
import { PeerRoomImpl } from '@/domain/impls/PeerRoom'
// import { PeerRoomImpl } from '@/domain/impls/PeerRoom2'
import '@/assets/styles/tailwind.css'
+import '@/assets/styles/sonner.css'
import { createElement } from '@/utils'
import { ToastImpl } from '@/domain/impls/Toast'
@@ -27,7 +28,6 @@ export default defineContentScript({
name: __NAME__,
position: 'inline',
anchor: 'body',
- isolateEvents: ['scroll', 'click'],
mode: __DEV__ ? 'open' : 'closed',
onMount: (container) => {
const app = createElement('
')
@@ -35,11 +35,11 @@ export default defineContentScript({
const root = createRoot(app)
root.render(
- //
-
-
-
- //
+
+
+
+
+
)
return root
},
diff --git a/src/app/content/views/AppButton/index.tsx b/src/app/content/views/AppButton/index.tsx
index 18f5b39..09efe3f 100644
--- a/src/app/content/views/AppButton/index.tsx
+++ b/src/app/content/views/AppButton/index.tsx
@@ -1,5 +1,6 @@
import { type ReactNode, type FC, useState, type MouseEvent, useRef } from 'react'
import { SettingsIcon, MoonIcon, SunIcon } from 'lucide-react'
+import { motion, AnimatePresence } from 'framer-motion'
import { browser } from 'wxt/browser'
import { useRemeshDomain, useRemeshQuery, useRemeshSend } from 'remesh-react'
@@ -25,14 +26,12 @@ const AppButton: FC = ({ children }) => {
const menuRef = useRef(null)
- /**
- * Waiting for PR merge
- * @see https://github.com/streamich/react-use/pull/2528
- */
useClickAway(menuRef, () => {
setOpen(false)
}, ['click'])
+ const handleToggleApp = () => {}
+
const handleToggleMenu = (e: MouseEvent) => {
e.preventDefault()
setOpen(!open)
@@ -52,35 +51,80 @@ const AppButton: FC = ({ children }) => {
return (
- {/*
*/}
-
-
-
-
-
diff --git a/src/app/content/views/Main/index.tsx b/src/app/content/views/Main/index.tsx
index b48f0ee..696fce2 100644
--- a/src/app/content/views/Main/index.tsx
+++ b/src/app/content/views/Main/index.tsx
@@ -7,6 +7,7 @@ import PromptItem from '../../components/PromptItem'
import UserInfoDomain from '@/domain/UserInfo'
import RoomDomain, { MessageType } from '@/domain/Room'
import MessageListDomain from '@/domain/MessageList'
+import BlurFade from '@/components/magicui/blur-fade'
const Main: FC = () => {
const send = useRemeshSend()
@@ -38,21 +39,24 @@ const Main: FC = () => {
{messageList.map((message, index) =>
message.type === MessageType.Normal ? (
- handleLikeChange(message.id)}
- onHateChange={() => handleHateChange(message.id)}
- >
+
+ handleLikeChange(message.id)}
+ onHateChange={() => handleHateChange(message.id)}
+ >
+
) : (
-
+
+
+
)
)}
diff --git a/src/app/content/views/Setup/index.tsx b/src/app/content/views/Setup/index.tsx
index 1474393..1fc2584 100644
--- a/src/app/content/views/Setup/index.tsx
+++ b/src/app/content/views/Setup/index.tsx
@@ -96,7 +96,7 @@ const Setup: FC = () => {
return (
-
+
diff --git a/src/app/options/components/ProfileForm.tsx b/src/app/options/components/ProfileForm.tsx
index b6dc4ac..b9d6ef5 100644
--- a/src/app/options/components/ProfileForm.tsx
+++ b/src/app/options/components/ProfileForm.tsx
@@ -16,6 +16,7 @@ import { RefreshCcwIcon } from 'lucide-react'
import { MAX_AVATAR_SIZE } from '@/constants/config'
import ToastDomain from '@/domain/Toast'
import BlurFade from '@/components/magicui/blur-fade'
+import debounce from './../../../utils/debounce'
const defaultUserInfo: UserInfo = {
id: nanoid(),
@@ -95,7 +96,7 @@ const ProfileForm = () => {
render={({ field }) => (
-
+
svg {
+ opacity: 0;
+ transform: scale(0.8);
+ transform-origin: center;
+ animation: sonner-fade-in 300ms ease forwards;
+}
+
+:where([data-sonner-toast]) :where([data-icon]) > * {
+ flex-shrink: 0;
+}
+
+:where([data-sonner-toast]) :where([data-icon]) svg {
+ margin-left: var(--toast-svg-margin-start);
+ margin-right: var(--toast-svg-margin-end);
+}
+
+:where([data-sonner-toast]) :where([data-content]) {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+[data-sonner-toast][data-styled='true'] [data-button] {
+ border-radius: 4px;
+ padding-left: 8px;
+ padding-right: 8px;
+ height: 24px;
+ font-size: 12px;
+ color: var(--normal-bg);
+ background: var(--normal-text);
+ margin-left: var(--toast-button-margin-start);
+ margin-right: var(--toast-button-margin-end);
+ border: none;
+ cursor: pointer;
+ outline: none;
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+ transition:
+ opacity 400ms,
+ box-shadow 200ms;
+}
+
+:where([data-sonner-toast]) :where([data-button]):focus-visible {
+ box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);
+}
+
+:where([data-sonner-toast]) :where([data-button]):first-of-type {
+ margin-left: var(--toast-button-margin-start);
+ margin-right: var(--toast-button-margin-end);
+}
+
+:where([data-sonner-toast]) :where([data-cancel]) {
+ color: var(--normal-text);
+ background: rgba(0, 0, 0, 0.08);
+}
+
+:where([data-sonner-toast][data-theme='dark']) :where([data-cancel]) {
+ background: rgba(255, 255, 255, 0.3);
+}
+
+:where([data-sonner-toast]) :where([data-close-button]) {
+ position: absolute;
+ left: var(--toast-close-button-start);
+ right: var(--toast-close-button-end);
+ top: 0;
+ height: 20px;
+ width: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 0;
+ background: var(--gray1);
+ color: var(--gray12);
+ border: 1px solid var(--gray4);
+ transform: var(--toast-close-button-transform);
+ border-radius: 50%;
+ cursor: pointer;
+ z-index: 1;
+ transition:
+ opacity 100ms,
+ background 200ms,
+ border-color 200ms;
+}
+
+:where([data-sonner-toast]) :where([data-close-button]):focus-visible {
+ box-shadow:
+ 0px 4px 12px rgba(0, 0, 0, 0.1),
+ 0 0 0 2px rgba(0, 0, 0, 0.2);
+}
+
+:where([data-sonner-toast]) :where([data-disabled='true']) {
+ cursor: not-allowed;
+}
+
+:where([data-sonner-toast]):hover :where([data-close-button]):hover {
+ background: var(--gray2);
+ border-color: var(--gray5);
+}
+
+/* Leave a ghost div to avoid setting hover to false when swiping out */
+:where([data-sonner-toast][data-swiping='true'])::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ right: 0;
+ height: 100%;
+ z-index: -1;
+}
+
+:where([data-sonner-toast][data-y-position='top'][data-swiping='true'])::before {
+ /* y 50% needed to distribute height additional height evenly */
+ bottom: 50%;
+ transform: scaleY(3) translateY(50%);
+}
+
+:where([data-sonner-toast][data-y-position='bottom'][data-swiping='true'])::before {
+ /* y -50% needed to distribute height additional height evenly */
+ top: 50%;
+ transform: scaleY(3) translateY(-50%);
+}
+
+/* Leave a ghost div to avoid setting hover to false when transitioning out */
+:where([data-sonner-toast][data-swiping='false'][data-removed='true'])::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ transform: scaleY(2);
+}
+
+/* Needed to avoid setting hover to false when inbetween toasts */
+:where([data-sonner-toast])::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ height: calc(var(--gap) + 1px);
+ bottom: 100%;
+ width: 100%;
+}
+
+:where([data-sonner-toast][data-mounted='true']) {
+ --y: translateY(0);
+ opacity: 1;
+}
+
+:where([data-sonner-toast][data-expanded='false'][data-front='false']) {
+ --scale: var(--toasts-before) * 0.05 + 1;
+ --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));
+ height: var(--front-toast-height);
+}
+
+:where([data-sonner-toast]) > * {
+ transition: opacity 400ms;
+}
+
+:where([data-sonner-toast][data-expanded='false'][data-front='false'][data-styled='true']) > * {
+ opacity: 0;
+}
+
+:where([data-sonner-toast][data-visible='false']) {
+ opacity: 0;
+ pointer-events: none;
+}
+
+:where([data-sonner-toast][data-mounted='true'][data-expanded='true']) {
+ --y: translateY(calc(var(--lift) * var(--offset)));
+ height: var(--initial-height);
+}
+
+:where([data-sonner-toast][data-removed='true'][data-front='true'][data-swipe-out='false']) {
+ --y: translateY(calc(var(--lift) * -100%));
+ opacity: 0;
+}
+
+:where([data-sonner-toast][data-removed='true'][data-front='false'][data-swipe-out='false'][data-expanded='true']) {
+ --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));
+ opacity: 0;
+}
+
+:where([data-sonner-toast][data-removed='true'][data-front='false'][data-swipe-out='false'][data-expanded='false']) {
+ --y: translateY(40%);
+ opacity: 0;
+ transition:
+ transform 500ms,
+ opacity 200ms;
+}
+
+/* Bump up the height to make sure hover state doesn't get set to false */
+:where([data-sonner-toast][data-removed='true'][data-front='false'])::before {
+ height: calc(var(--initial-height) + 20%);
+}
+
+[data-sonner-toast][data-swiping='true'] {
+ transform: var(--y) translateY(var(--swipe-amount, 0px));
+ transition: none;
+}
+
+[data-sonner-toast][data-swipe-out='true'][data-y-position='bottom'],
+[data-sonner-toast][data-swipe-out='true'][data-y-position='top'] {
+ animation: swipe-out 200ms ease-out forwards;
+}
+
+@keyframes swipe-out {
+ from {
+ transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));
+ opacity: 1;
+ }
+
+ to {
+ transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%));
+ opacity: 0;
+ }
+}
+
+@media (max-width: 600px) {
+ [data-sonner-toaster] {
+ position: fixed;
+ --mobile-offset: 16px;
+ right: var(--mobile-offset);
+ left: var(--mobile-offset);
+ width: 100%;
+ }
+
+ [data-sonner-toaster][dir='rtl'] {
+ left: calc(var(--mobile-offset) * -1);
+ }
+
+ [data-sonner-toaster] [data-sonner-toast] {
+ left: 0;
+ right: 0;
+ width: calc(100% - var(--mobile-offset) * 2);
+ }
+
+ [data-sonner-toaster][data-x-position='left'] {
+ left: var(--mobile-offset);
+ }
+
+ [data-sonner-toaster][data-y-position='bottom'] {
+ bottom: 20px;
+ }
+
+ [data-sonner-toaster][data-y-position='top'] {
+ top: 20px;
+ }
+
+ [data-sonner-toaster][data-x-position='center'] {
+ left: var(--mobile-offset);
+ right: var(--mobile-offset);
+ transform: none;
+ }
+}
+
+[data-sonner-toaster][data-theme='light'] {
+ --normal-bg: #fff;
+ --normal-border: var(--gray4);
+ --normal-text: var(--gray12);
+
+ --success-bg: hsl(143, 85%, 96%);
+ --success-border: hsl(145, 92%, 91%);
+ --success-text: hsl(140, 100%, 27%);
+
+ --info-bg: hsl(208, 100%, 97%);
+ --info-border: hsl(221, 91%, 91%);
+ --info-text: hsl(210, 92%, 45%);
+
+ --warning-bg: hsl(49, 100%, 97%);
+ --warning-border: hsl(49, 91%, 91%);
+ --warning-text: hsl(31, 92%, 45%);
+
+ --error-bg: hsl(359, 100%, 97%);
+ --error-border: hsl(359, 100%, 94%);
+ --error-text: hsl(360, 100%, 45%);
+}
+
+[data-sonner-toaster][data-theme='light'] [data-sonner-toast][data-invert='true'] {
+ --normal-bg: #000;
+ --normal-border: hsl(0, 0%, 20%);
+ --normal-text: var(--gray1);
+}
+
+[data-sonner-toaster][data-theme='dark'] [data-sonner-toast][data-invert='true'] {
+ --normal-bg: #fff;
+ --normal-border: var(--gray3);
+ --normal-text: var(--gray12);
+}
+
+[data-sonner-toaster][data-theme='dark'] {
+ --normal-bg: #000;
+ --normal-border: hsl(0, 0%, 20%);
+ --normal-text: var(--gray1);
+
+ --success-bg: hsl(150, 100%, 6%);
+ --success-border: hsl(147, 100%, 12%);
+ --success-text: hsl(150, 86%, 65%);
+
+ --info-bg: hsl(215, 100%, 6%);
+ --info-border: hsl(223, 100%, 12%);
+ --info-text: hsl(216, 87%, 65%);
+
+ --warning-bg: hsl(64, 100%, 6%);
+ --warning-border: hsl(60, 100%, 12%);
+ --warning-text: hsl(46, 87%, 65%);
+
+ --error-bg: hsl(358, 76%, 10%);
+ --error-border: hsl(357, 89%, 16%);
+ --error-text: hsl(358, 100%, 81%);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='success'] {
+ background: var(--success-bg);
+ border-color: var(--success-border);
+ color: var(--success-text);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='success'] [data-close-button] {
+ background: var(--success-bg);
+ border-color: var(--success-border);
+ color: var(--success-text);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='info'] {
+ background: var(--info-bg);
+ border-color: var(--info-border);
+ color: var(--info-text);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='info'] [data-close-button] {
+ background: var(--info-bg);
+ border-color: var(--info-border);
+ color: var(--info-text);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='warning'] {
+ background: var(--warning-bg);
+ border-color: var(--warning-border);
+ color: var(--warning-text);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='warning'] [data-close-button] {
+ background: var(--warning-bg);
+ border-color: var(--warning-border);
+ color: var(--warning-text);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='error'] {
+ background: var(--error-bg);
+ border-color: var(--error-border);
+ color: var(--error-text);
+}
+
+[data-rich-colors='true'][data-sonner-toast][data-type='error'] [data-close-button] {
+ background: var(--error-bg);
+ border-color: var(--error-border);
+ color: var(--error-text);
+}
+
+.sonner-loading-wrapper {
+ --size: 16px;
+ height: var(--size);
+ width: var(--size);
+ position: absolute;
+ inset: 0;
+ z-index: 10;
+}
+
+.sonner-loading-wrapper[data-visible='false'] {
+ transform-origin: center;
+ animation: sonner-fade-out 0.2s ease forwards;
+}
+
+.sonner-spinner {
+ position: relative;
+ top: 50%;
+ left: 50%;
+ height: var(--size);
+ width: var(--size);
+}
+
+.sonner-loading-bar {
+ animation: sonner-spin 1.2s linear infinite;
+ background: var(--gray11);
+ border-radius: 6px;
+ height: 8%;
+ left: -10%;
+ position: absolute;
+ top: -3.9%;
+ width: 24%;
+}
+
+.sonner-loading-bar:nth-child(1) {
+ animation-delay: -1.2s;
+ transform: rotate(0.0001deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(2) {
+ animation-delay: -1.1s;
+ transform: rotate(30deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(3) {
+ animation-delay: -1s;
+ transform: rotate(60deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(4) {
+ animation-delay: -0.9s;
+ transform: rotate(90deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(5) {
+ animation-delay: -0.8s;
+ transform: rotate(120deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(6) {
+ animation-delay: -0.7s;
+ transform: rotate(150deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(7) {
+ animation-delay: -0.6s;
+ transform: rotate(180deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(8) {
+ animation-delay: -0.5s;
+ transform: rotate(210deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(9) {
+ animation-delay: -0.4s;
+ transform: rotate(240deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(10) {
+ animation-delay: -0.3s;
+ transform: rotate(270deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(11) {
+ animation-delay: -0.2s;
+ transform: rotate(300deg) translate(146%);
+}
+
+.sonner-loading-bar:nth-child(12) {
+ animation-delay: -0.1s;
+ transform: rotate(330deg) translate(146%);
+}
+
+@keyframes sonner-fade-in {
+ 0% {
+ opacity: 0;
+ transform: scale(0.8);
+ }
+ 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes sonner-fade-out {
+ 0% {
+ opacity: 1;
+ transform: scale(1);
+ }
+ 100% {
+ opacity: 0;
+ transform: scale(0.8);
+ }
+}
+
+@keyframes sonner-spin {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0.15;
+ }
+}
+
+@media (prefers-reduced-motion) {
+ [data-sonner-toast],
+ [data-sonner-toast] > *,
+ .sonner-loading-bar {
+ transition: none !important;
+ animation: none !important;
+ }
+}
+
+.sonner-loader {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ transform-origin: center;
+ transition:
+ opacity 200ms,
+ transform 200ms;
+}
+
+.sonner-loader[data-visible='false'] {
+ opacity: 0;
+ transform: scale(0.8) translate(-50%, -50%);
+}
+
+/* Custom styles */
+:where([data-sonner-toaster]) {
+ width: 200px;
+ position: absolute;
+}
+
+:where([data-sonner-toast][data-styled='true']) {
+ width: 200px;
+ padding: 6px 12px;
+ border-radius: 9999px;
+}
diff --git a/src/domain/Room.ts b/src/domain/Room.ts
index c10754a..c0ad034 100644
--- a/src/domain/Room.ts
+++ b/src/domain/Room.ts
@@ -7,6 +7,7 @@ import UserInfoDomain from '@/domain/UserInfo'
import { desert, upsert } from '@/utils'
import { nanoid } from 'nanoid'
import StatusModule from '@/domain/modules/Status'
+import { ToastExtern } from './externs/Toast'
export { MessageType }
@@ -50,6 +51,7 @@ const RoomDomain = Remesh.domain({
const messageListDomain = domain.getDomain(MessageListDomain())
const userInfoDomain = domain.getDomain(UserInfoDomain())
const peerRoom = domain.getExtern(PeerRoomExtern)
+ const toast = domain.getExtern(ToastExtern)
const PeerIdState = domain.state({
name: 'Room.PeerIdState',
@@ -389,7 +391,21 @@ const RoomDomain = Remesh.domain({
}
})
- // 以后移动到 service worker 中,无需每次刷新页面都发送离开房间的消息
+ domain.effect({
+ name: 'RoomOnErrorEffect',
+ impl: () => {
+ const onRoomError$ = fromEventPattern(peerRoom.onError).pipe(
+ map((error) => {
+ console.error(error)
+ toast.error(error.message)
+ return null
+ })
+ )
+ return onRoomError$
+ }
+ })
+
+ // TODO: Move this to a service worker in the future, so we don't need to send a leave room message every time the page refreshes
domain.effect({
name: 'RoomOnUnloadEffect',
impl: () => {
diff --git a/src/domain/impls/PeerRoom.ts b/src/domain/impls/PeerRoom.ts
index 307357d..759015f 100644
--- a/src/domain/impls/PeerRoom.ts
+++ b/src/domain/impls/PeerRoom.ts
@@ -50,10 +50,10 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ const [send] = this.room.makeAction('MESSAGE')
+ send(message as DataPayload, id)
}
- const [send] = this.room.makeAction('MESSAGE')
- send(message as DataPayload, id)
})
} else {
const [send] = this.room.makeAction('MESSAGE')
@@ -69,10 +69,10 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ const [, on] = this.room.makeAction('MESSAGE')
+ on((message) => callback(message as T))
}
- const [, on] = this.room.makeAction('MESSAGE')
- on((message) => callback(message as T))
})
} else {
const [, on] = this.room.makeAction('MESSAGE')
@@ -88,10 +88,11 @@ class PeerRoom extends EventHub {
const error = new Error('Room not joined')
this.emit('error', error)
throw error
+ } else {
+ this.room.onPeerJoin((peerId) => {
+ callback(peerId)
+ })
}
- this.room.onPeerJoin((peerId) => {
- callback(peerId)
- })
})
} else {
this.room.onPeerJoin((peerId) => {
@@ -107,9 +108,9 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ this.room.onPeerLeave((peerId) => callback(peerId))
}
- this.room.onPeerLeave((peerId) => callback(peerId))
})
} else {
this.room.onPeerLeave((peerId) => callback(peerId))
@@ -123,10 +124,10 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ this.room.leave()
+ this.room = undefined
}
- this.room.leave()
- this.room = undefined
})
} else {
this.room.leave()
diff --git a/src/domain/impls/PeerRoom2.ts b/src/domain/impls/PeerRoom2.ts
index 86f1a2d..8614594 100644
--- a/src/domain/impls/PeerRoom2.ts
+++ b/src/domain/impls/PeerRoom2.ts
@@ -49,9 +49,9 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ this.room.send(JSON.stringify(message), id)
}
- this.room.send(JSON.stringify(message), id)
})
} else {
this.room.send(JSON.stringify(message), id)
@@ -65,9 +65,9 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ this.room.on('message', (message) => callback(JSON.parse(message) as T))
}
- this.room.on('message', (message) => callback(JSON.parse(message) as T))
})
} else {
this.room.on('message', (message) => callback(JSON.parse(message) as T))
@@ -81,9 +81,9 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ this.room.on('join', (id) => callback(id))
}
- this.room.on('join', (id) => callback(id))
})
} else {
this.room.on('join', (id) => callback(id))
@@ -97,9 +97,9 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ this.room.on('leave', (id) => callback(id))
}
- this.room.on('leave', (id) => callback(id))
})
} else {
this.room.on('leave', (id) => callback(id))
@@ -113,10 +113,10 @@ class PeerRoom extends EventHub {
if (!this.room) {
const error = new Error('Room not joined')
this.emit('error', error)
- throw error
+ } else {
+ this.room.leave()
+ this.room = undefined
}
- this.room.leave()
- this.room = undefined
})
} else {
this.room.leave()
diff --git a/src/hooks/useClickAway.ts b/src/hooks/useClickAway.ts
index 820c6ec..ae94ceb 100644
--- a/src/hooks/useClickAway.ts
+++ b/src/hooks/useClickAway.ts
@@ -2,6 +2,10 @@ import { type RefObject, useEffect, useRef } from 'react'
export type Events = Array
+/**
+ * Waiting for PR merge
+ * @see https://github.com/streamich/react-use/pull/2528
+ */
const useClickAway = (
ref: RefObject,
onClickAway: (event: E) => void,
diff --git a/tailwind.config.ts b/tailwind.config.ts
index e9ff6c6..e0516ac 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -73,21 +73,12 @@ export default {
pulse: {
'0%, 100%': { boxShadow: '0 0 0 0 var(--pulse-color)' },
'50%': { boxShadow: '0 0 0 8px var(--pulse-color)' }
- },
- ripple: {
- '0%, 100%': {
- transform: 'translate(-50%, -50%) scale(1)'
- },
- '50%': {
- transform: 'translate(-50%, -50%) scale(0.9)'
- }
}
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
- pulse: 'pulse var(--duration) ease-out infinite',
- ripple: 'ripple var(--duration,2s) ease calc(var(--i, 0)*.2s) infinite'
+ pulse: 'pulse var(--duration) ease-out infinite'
}
}
},